﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область СлужебныйПрограммныйИнтерфейс

// Проверить необходимость обновления неразделенных данных информационной базы
// при смене версии конфигурации.
//
Функция НеобходимоОбновлениеНеразделенныхДанныхИнформационнойБазы() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		
		ВерсияМетаданных = Метаданные.Версия;
		Если ПустаяСтрока(ВерсияМетаданных) Тогда
			ВерсияМетаданных = "0.0.0.0";
		КонецЕсли;
		
		ВерсияОбщихДанных = ВерсияИБ(Метаданные.Имя, Истина);
		
		Если НеобходимоВыполнитьОбновление(ВерсияМетаданных, ВерсияОбщихДанных) Тогда
			Возврат Истина;
		КонецЕсли;
		
		Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных()
		   И УстановленоЗапуститьОбновлениеИнформационнойБазы() Тогда
			
			Возврат Истина;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Возвращается Истина, если успешно завершены отложенные обработчики обновления
// в локальном режиме (причем без учета главного узла в подчиненном узле РИБ) или
// во всех областях данных в режиме сервиса.
//
// Параметры:
//  ПрогрессОбновления - Структура - возвращаемое значение, смотри
//                         ОбновлениеИнформационнойБазы.ПрогрессОбновленияОбластейДанных.
//
// Возвращаемое значение:
//  Булево
//
Функция ОтложенноеОбновлениеЗавершено(ПрогрессОбновления = Неопределено) Экспорт
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		ПрогрессОбновления = ОбновлениеИнформационнойБазы.ПрогрессОбновленияОбластейДанных("Отложенное");
		Возврат ПрогрессОбновления = Неопределено Или ПрогрессОбновления.Выполняется = 0
			И ПрогрессОбновления.Ожидают = 0 И ПрогрессОбновления.Проблемы = 0;
	КонецЕсли;
	
	Возврат ПолучитьФункциональнуюОпцию("ОтложенноеОбновлениеЗавершеноУспешно");
	
КонецФункции

// Выполнить неинтерактивное обновление данных ИБ.
//
// Параметры:
// 
//  ПараметрыОбновления - Структура:
//    * ИсключениеПриНевозможностиБлокировкиИБ - Булево - если Ложь, тогда при неудачной
//                 попытке установки монопольного режима исключение не вызывается,
//                 а возвращается строка "ОшибкаУстановкиМонопольногоРежима".
// 
//    * ПриЗапускеКлиентскогоПриложения - Булево - начальное значение Ложь. Если указать Истина,
//                 тогда параметры работы программы не будут обновляться, т.к. при клиентском
//                 запуске они обновляются в самом начале (до авторизации пользователи и обновления ИБ).
//                 Параметр требуется для оптимизации клиентского режима запуска, чтобы не выполнять
//                 обновление параметров работы программы дважды.
//                 При внешнем вызове, например, в сеансе внешнего соединения, параметры работы
//                 программы должны быть обновлены до продолжения обновления ИБ.
//    * Перезапустить             - Булево    - возвращаемое значение. Требование перезапуска,
//                                  в некоторых случаях ПриЗапускеКлиентскогоПриложения, например,
//                                  при возврате к конфигурации базы данных подчиненного узла РИБ,
//                                  см. общий модуль ОбменДаннымиСервер процедуру.
//                                  ВыполнитьСинхронизациюПриОтсутствииОбновленияИнформационнойБазы.
//    * УстановленнаяБлокировкаИБ - см. БлокировкаИБ
//    * ВФоне                     - Булево    - если обновление информационной базы выполняется
//                 в фоне, то следует передавать Истина, иначе Ложь.
//    * ВыполнятьОтложенныеОбработчики - Булево - если Истина, отложенное обновление будет выполнено
//                 в основном цикле обновления. Только для клиент-серверного режима работы.
// 
// Возвращаемое значение:
//  Строка -  признак выполнения обработчиков обновления:
//           "Успешно", "НеТребуется", "ОшибкаУстановкиМонопольногоРежима".
//
Функция ВыполнитьОбновлениеИнформационнойБазы(ПараметрыОбновления) Экспорт
	
	ДополнительныеПараметры = ДействияПередОбновлениемИнформационнойБазы(ПараметрыОбновления);
	Если ЗначениеЗаполнено(ДополнительныеПараметры.Возврат) Тогда
		Возврат ДополнительныеПараметры.Возврат;
	КонецЕсли;
	
	ПараметрЗапускаКлиента       = СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере().Получить("ПараметрЗапуска");
	ОтладкаОтложенногоОбновления = СтрНайти(НРег(ПараметрЗапускаКлиента), НРег("ОтладкаОтложенногоОбновления")) > 0;
	РежимВыполненияОтложенногоОбновления = РежимВыполненияОтложенногоОбновления(ПараметрыОбновления);
	ВыполнитьОтложенноеОбновлениеСейчас  = Не ОтладкаОтложенногоОбновления И РежимВыполненияОтложенногоОбновления = "Монопольно";
	
	ДополнительныеПараметры.Вставить("ВыполнитьОтложенноеОбновлениеСейчас", ВыполнитьОтложенноеОбновлениеСейчас);
	ДополнительныеПараметры.Вставить("РежимВыполненияОтложенногоОбновления", РежимВыполненияОтложенногоОбновления);
	
	ВыполнитьДействияПриОбновленииИнформационнойБазы(ПараметрыОбновления, ДополнительныеПараметры);
	ВыполнитьДействияПослеОбновленияИнформационнойБазы(ПараметрыОбновления, ДополнительныеПараметры);
	
	Возврат "Успешно";
	
КонецФункции

// Получить версию конфигурации или родительской конфигурации (библиотеки),
// которая хранится в информационной базе.
//
// Параметры:
//  ИдентификаторБиблиотеки   - Строка - имя конфигурации или идентификатор библиотеки.
//                            - Неопределено - будет получены версии всех подсистем.
//  ПолучитьВерсиюОбщихДанных - Булево - если указать Истина, то при работе в модели сервиса будет 
//                                       возвращена версия в общих данных.
//
// Возвращаемое значение:
//   Строка   - версия.
//   Соответствие из КлючИЗначение - если параметр ИдентификаторБиблиотеки неопределено:
//      * Ключ - Строка - имя подсистемы
//      * Значение - Строка - версия подсистемы
//
// Пример использования:
//   ВерсияКонфигурацииИБ = ВерсияИБ(Метаданные.Имя);
//
Функция ВерсияИБ(Знач ИдентификаторБиблиотеки, Знач ПолучитьВерсиюОбщихДанных = Ложь) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	СтандартнаяОбработка = Истина;
	Результат = "";
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ПриОпределенииВерсииИБ(ИдентификаторБиблиотеки, ПолучитьВерсиюОбщихДанных,
			СтандартнаяОбработка, Результат);
		
	КонецЕсли;
	
	ВсеВерсииПодсистем = (ИдентификаторБиблиотеки = Неопределено);
	Если ВсеВерсииПодсистем И Не СтандартнаяОбработка Тогда
		Возврат Результат;
	КонецЕсли;
	
	Если СтандартнаяОбработка Тогда
		
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ВерсииПодсистем.Версия,
		|	ВерсииПодсистем.ИмяПодсистемы
		|ИЗ
		|	РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем
		|ГДЕ
		|	&Условие";
		
		Если ВсеВерсииПодсистем Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Условие", "ИСТИНА");
		Иначе
			Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Условие", "ВерсииПодсистем.ИмяПодсистемы = &ИмяПодсистемы");
			Запрос.УстановитьПараметр("ИмяПодсистемы", ИдентификаторБиблиотеки);
		КонецЕсли;
		ТаблицаЗначений = Запрос.Выполнить().Выгрузить();
		Результат = "";
		Если ТаблицаЗначений.Количество() > 0 Тогда
			Если Не ВсеВерсииПодсистем Тогда
				Результат = СокрЛП(ТаблицаЗначений[0].Версия);
			Иначе
				ВерсииПодсистем = Новый Соответствие;
				Для Каждого СтрокаВерсия Из ТаблицаЗначений Цикл
					ВерсииПодсистем.Вставить(СтрокаВерсия.ИмяПодсистемы, СокрЛП(СтрокаВерсия.Версия));
				КонецЦикла;
				
				Возврат ВерсииПодсистем;
			КонецЕсли;
		КонецЕсли;
		
	КонецЕсли;
	
	Если ВсеВерсииПодсистем Тогда
		Возврат Неопределено;
	Иначе
		Возврат ?(ПустаяСтрока(Результат), "0.0.0.0", Результат);
	КонецЕсли;
	
КонецФункции

// Записывает в информационную базу версию конфигурации или родительской конфигурации (библиотеки).
//
// Параметры:
//  ИдентификаторБиблиотеки - Строка - имя конфигурации или родительской конфигурации (библиотеки),
//  НомерВерсии             - Строка - номер версии.
//  ЭтоОсновнаяКонфигурация - Булево - признак, что ИдентификаторБиблиотеки соответствует имени конфигурации.
//  ВыполненаРегистрация    - Булево - признак, что выполнена регистрация данных для отложенного обновления.
//
Процедура УстановитьВерсиюИБ(Знач ИдентификаторБиблиотеки, Знач НомерВерсии, Знач ЭтоОсновнаяКонфигурация, ВыполненаРегистрация = Неопределено) Экспорт
	
	СтандартнаяОбработка = Истина;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ПриУстановкеВерсииИБ(ИдентификаторБиблиотеки, НомерВерсии, СтандартнаяОбработка, ЭтоОсновнаяКонфигурация);
		
	КонецЕсли;
	
	Если Не СтандартнаяОбработка Тогда
		Возврат;
	КонецЕсли;
		
	НаборЗаписей = РегистрыСведений.ВерсииПодсистем.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.ИмяПодсистемы.Установить(ИдентификаторБиблиотеки);
	
	НоваяЗапись = НаборЗаписей.Добавить();
	НоваяЗапись.ИмяПодсистемы = ИдентификаторБиблиотеки;
	НоваяЗапись.Версия = НомерВерсии;
	НоваяЗапись.ЭтоОсновнаяКонфигурация = ЭтоОсновнаяКонфигурация;
	Если ВыполненаРегистрация <> Неопределено Тогда
		НоваяЗапись.ВыполненаРегистрацияОтложенныхОбработчиков = ВыполненаРегистрация;
	КонецЕсли;
	
	НаборЗаписей.Записать();
	
КонецПроцедуры

// Записывает информацию о результате регистрации отложенных обработчиков на плане обмена.
//
Процедура ОтметитьРегистрациюОтложенныхОбработчиковОбновления(ИмяПодсистемы = Неопределено, Значение = Истина) Экспорт
	
	СтандартнаяОбработка = Истина;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ПриОтметкеРегистрацииОтложенныхОбработчиковОбновления(ИмяПодсистемы, Значение, СтандартнаяОбработка);
	КонецЕсли;
	
	Если Не СтандартнаяОбработка Тогда
		Возврат;
	КонецЕсли;
	
	НаборЗаписей = РегистрыСведений.ВерсииПодсистем.СоздатьНаборЗаписей();
	Если ИмяПодсистемы <> Неопределено Тогда
		НаборЗаписей.Отбор.ИмяПодсистемы.Установить(ИмяПодсистемы);
	КонецЕсли;
	НаборЗаписей.Прочитать();
	
	Если НаборЗаписей.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Для Каждого ЗаписьРегистра Из НаборЗаписей Цикл
		ЗаписьРегистра.ВыполненаРегистрацияОтложенныхОбработчиков = Значение;
	КонецЦикла;
	НаборЗаписей.Записать();
	
КонецПроцедуры

// Возвращает режим обновления данных информационной базы.
// Допускается вызывать только до начала обновления информационной базы (иначе возвращает "ОбновлениеВерсии").
// 
// Возвращаемое значение:
//   Строка   - "НачальноеЗаполнение", если это первый запуск пустой базы (области данных);
//              "ОбновлениеВерсии", если выполняется первый запуск после обновление конфигурации базы данных;
//              "ПереходСДругойПрограммы", если выполняется первый запуск после обновление конфигурации базы данных, 
//              в которой изменилось имя основной конфигурации.
//
Функция РежимОбновленияДанных() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	СтандартнаяОбработка = Истина;
	РежимОбновленияДанных = "";
	
	ИмяОсновнойКонфигурации = Метаданные.Имя;
	ОписанияПодсистем  = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем();
	Для каждого ИмяПодсистемы Из ОписанияПодсистем.Порядок Цикл
		ОписаниеПодсистемы = ОписанияПодсистем.ПоИменам.Получить(ИмяПодсистемы); // см. СтандартныеПодсистемыПовтИсп.НовоеОписаниеПодсистемы
		Если НЕ ЗначениеЗаполнено(ОписаниеПодсистемы.ОсновнойСерверныйМодуль) Тогда
			Продолжить;
		КонецЕсли;
		
		Если ОписаниеПодсистемы.Имя <> ИмяОсновнойКонфигурации Тогда
			Продолжить;
		КонецЕсли;
		
		Модуль = ОбщегоНазначения.ОбщийМодуль(ОписаниеПодсистемы.ОсновнойСерверныйМодуль);
		Модуль.ПриОпределенииРежимаОбновленияДанных(РежимОбновленияДанных, СтандартнаяОбработка);
	КонецЦикла;
	
	Если НЕ СтандартнаяОбработка Тогда
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("ПриОпределенииРежимаОбновленияДанных", "РежимОбновленияДанных",
			РежимОбновленияДанных, Тип("Строка"));
		Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Недопустимое значение параметра %1 в %2. 
			|Ожидалось: %3, %4 или %5; передано значение: %6 (тип %7).'"),
			"РежимОбновленияДанных", "ПриОпределенииРежимаОбновленияДанных",
			"НачальноеЗаполнение", "ОбновлениеВерсии", "ПереходСДругойПрограммы",
			РежимОбновленияДанных, ТипЗнч(РежимОбновленияДанных));
		ОбщегоНазначенияКлиентСервер.Проверить(РежимОбновленияДанных = "НачальноеЗаполнение" 
			Или РежимОбновленияДанных = "ОбновлениеВерсии" Или РежимОбновленияДанных = "ПереходСДругойПрограммы", Сообщение);
		Возврат РежимОбновленияДанных;
	КонецЕсли;

	Результат = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ПриОпределенииРежимаОбновленияОбластиДанных(СтандартнаяОбработка, Результат);
	КонецЕсли;
	
	Если НЕ СтандартнаяОбработка Тогда
		Возврат Результат;
	КонецЕсли;
	
	Возврат РежимОбновленияДанныхВЛокальномРежимеРаботы();
	
КонецФункции

// Для внутреннего использования.
Функция ПараметрыОтбораОбработчиков() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ПолучатьРазделенные", Ложь);
	Результат.Вставить("РежимОбновления", "Монопольно");
	Результат.Вставить("УчитыватьПервыйОбменВРИБ", Ложь);
	Результат.Вставить("ПервыйОбменВРИБ", Ложь);
	Возврат Результат;
	
КонецФункции

Функция ОбработчикиОбновленияВИнтервале(Знач ИсходнаяТаблицаОбработчиков, Знач ВерсияОт, Знач ВерсияДо,
		Знач ПараметрыОтбораОбработчиков = Неопределено) Экспорт
	
	ПараметрыОтбора = ПараметрыОтбораОбработчиков;
	Если ПараметрыОтбора = Неопределено Тогда
		ПараметрыОтбора = ПараметрыОтбораОбработчиков();
	КонецЕсли;
	// Добавление номера в таблицу, для упорядочивания в порядке добавления.
	ВсеОбработчики = ИсходнаяТаблицаОбработчиков.Скопировать();
	
	ВсеОбработчики.Колонки.Добавить("НомерПоПорядку", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0)));
	Для Индекс = 0 По ВсеОбработчики.Количество() - 1 Цикл
		СтрокаОбработчика = ВсеОбработчики[Индекс];
		СтрокаОбработчика.НомерПоПорядку = Индекс + 1;
	КонецЦикла;
	
	ОтметитьОбработчикиНовыхПодсистем(ВсеОбработчики);
	
	// Подготовка параметров
	ВыбиратьРазделенныеОбработчики = Истина;
	ВыбиратьНеразделенныеОбработчики = Истина;
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Если ПараметрыОтбора.ПолучатьРазделенные Тогда
			ВыбиратьНеразделенныеОбработчики = Ложь;
		Иначе
			Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
				ВыбиратьНеразделенныеОбработчики = Ложь;
			Иначе
				ВыбиратьРазделенныеОбработчики = Ложь;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	// Формирование дерева обработчиков.
	Схема = ПолучитьОбщийМакет("ПолучениеДереваОбработчиковОбновления");
	Схема.Параметры.Найти("ВыбиратьРазделенныеОбработчики").Значение = ВыбиратьРазделенныеОбработчики;
	Схема.Параметры.Найти("ВыбиратьНеразделенныеОбработчики").Значение = ВыбиратьНеразделенныеОбработчики;
	Схема.Параметры.Найти("ВерсияОт").Значение = ВерсияОт;
	Схема.Параметры.Найти("ВерсияДо").Значение = ВерсияДо;
	Схема.Параметры.Найти("ВесВерсииОт").Значение = ВесВерсии(Схема.Параметры.Найти("ВерсияОт").Значение);
	Схема.Параметры.Найти("ВесВерсииДо").Значение = ВесВерсии(Схема.Параметры.Найти("ВерсияДо").Значение);
	Схема.Параметры.Найти("ОперативноеОбновление").Значение = (ПараметрыОтбора.РежимОбновления = "Оперативно");
	Схема.Параметры.Найти("ОтложенноеОбновление").Значение = (ПараметрыОтбора.РежимОбновления = "Отложенно");
	Если ПараметрыОтбора.УчитыватьПервыйОбменВРИБ Тогда
		Схема.Параметры.Найти("ПервыйОбменВРИБ").Значение = ПараметрыОтбора.ПервыйОбменВРИБ;
		Схема.Параметры.Найти("ЭтоРИБСФильтром").Значение = СтандартныеПодсистемыПовтИсп.ИспользуетсяРИБ("СФильтром");
	КонецЕсли;
	
	Компоновщик = Новый КомпоновщикМакетаКомпоновкиДанных;
	Макет = Компоновщик.Выполнить(Схема, Схема.НастройкиПоУмолчанию, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
	
	ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновки.Инициализировать(Макет, Новый Структура("Обработчики", ВсеОбработчики), , Истина);
	
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	ПроцессорВывода.УстановитьОбъект(Новый ДеревоЗначений);
	
	ВыполняемыеОбработчики = ПроцессорВывода.Вывести(ПроцессорКомпоновки);
	
	КолонкаВерсия = ВыполняемыеОбработчики.Колонки.Версия; // КоллекцияКолонокТаблицыЗначений
	КолонкаВерсия.Имя = "ВерсияРегистрации";
	КолонкаГруппаВерсии = ВыполняемыеОбработчики.Колонки.ГруппаВерсии;  // КоллекцияКолонокТаблицыЗначений
	КолонкаГруппаВерсии.Имя = "Версия";
	
	// Сортировка обработчиков по признаку "ОбщиеДанные".
	Для Каждого Версия Из ВыполняемыеОбработчики.Строки Цикл
		Версия.Строки.Сортировать("ОбщиеДанные Убыв", Истина);
	КонецЦикла;
	
	Возврат ВыполняемыеОбработчики;
	
КонецФункции

// Для внутреннего использования.
//
Функция НеобходимоВыполнитьОбновление(Знач ВерсияМетаданных, Знач ВерсияДанных) Экспорт
	Возврат НЕ ПустаяСтрока(ВерсияМетаданных) И ВерсияДанных <> ВерсияМетаданных;
КонецФункции

// Для внутреннего использования.
//
Функция ВыполненаРегистрацияОтложенныхОбработчиковОбновления() Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Если ОбщегоНазначения.РазделениеВключено()
		И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат Истина; // В неразделенном режиме отложенное обновление не выполняется.
	КонецЕсли;
	
	СтандартнаяОбработка = Истина;
	Результат = "";
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ПриПроверкеРегистрацииОтложенныхОбработчиковОбновления(Результат, СтандартнаяОбработка);
	КонецЕсли;
	
	Если Не СтандартнаяОбработка Тогда
		Возврат Результат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ВерсииПодсистем.ИмяПодсистемы
		|ИЗ
		|	РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем
		|ГДЕ
		|	ВерсииПодсистем.ВыполненаРегистрацияОтложенныхОбработчиков = ЛОЖЬ";
	
	Результат = Запрос.Выполнить().Выгрузить();
	Возврат Результат.Количество() = 0;
	
КонецФункции

// Возвращает Истина, если у пользователя не отключен показ описания изменений
// системы после обновления и есть непоказанные изменения.
//
Функция ПоказатьОписаниеИзмененийСистемы() Экспорт
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	Если СведенияОбОбновлении.ВыводитьОписаниеОбновлений = Ложь Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Не ПравоДоступа("СохранениеДанныхПользователя", Метаданные) Тогда
		// Анонимным пользователям новое в версии не показываем.
		Возврат Ложь;
	КонецЕсли;
	
	Если Не ПравоДоступа("Просмотр", Метаданные.ОбщиеФормы.ОписаниеИзмененийПрограммы) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено()
		И Пользователи.ЭтоПолноправныйПользователь(, Истина) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ВывестиОписаниеИзмененийДляАдминистратора = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить("ОбновлениеИБ", "ВывестиОписаниеИзмененийДляАдминистратора",,, ИмяПользователя());
	Если ВывестиОписаниеИзмененийДляАдминистратора = Истина Тогда
		Возврат Истина;
	КонецЕсли;
	
	ПоследняяВерсия = ПоследняяВерсияОтображенияИзмененийСистемы();
	Если ПоследняяВерсия = Неопределено Тогда
		Возврат Истина;
	КонецЕсли;
	
	Разделы = РазделыОписанияИзменений();
	
	Если Разделы = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат ПолучитьВерсииБольшеЗаданной(Разделы, ПоследняяВерсия).Количество() > 0;
	
КонецФункции

// Проверяет статус отложенных обработчиков обновления.
//
Функция СтатусНевыполненныхОбработчиков(ПриОбновлении = Ложь) Экспорт
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	
	Если ПриОбновлении Тогда
		ВерсияДанных = ВерсияИБ(Метаданные.Имя);
		ВерсияДанныхБезНомераСборки = ОбщегоНазначенияКлиентСервер.ВерсияКонфигурацииБезНомераСборки(ВерсияДанных);
		ВерсияМетаданныхБезНомераСборки = ОбщегоНазначенияКлиентСервер.ВерсияКонфигурацииБезНомераСборки(Метаданные.Версия);
		ПодредакцииРавны = (ВерсияДанныхБезНомераСборки = ВерсияМетаданныхБезНомераСборки);
		
		Если ВерсияДанных = "0.0.0.0" Или ПодредакцииРавны Тогда
			// В пределах четвертой цифры можно обновляться при наличии невыполненных
			// отложенных обработчиков обновления.
			Возврат "";
		КонецЕсли;
		
		ВерсияДереваОбработчиков = СведенияОбОбновлении.ВерсияДереваОбработчиков;
		Если ВерсияДереваОбработчиков <> "" И ОбщегоНазначенияКлиентСервер.СравнитьВерсии(ВерсияДереваОбработчиков, ВерсияДанных) > 0 Тогда
			// Если в основном цикле обновления произошла ошибка, то при перезапуске не надо
			// проверять дерево отложенных обработчиков, т.к. там будут еще невыполненные
			// отложенные обработчики на текущую версию.
			Возврат "";
		КонецЕсли;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.Статус КАК Статус
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|
		|СГРУППИРОВАТЬ ПО
		|	ОбработчикиОбновления.Статус";
	СтатусыОбработчиков = Запрос.Выполнить().Выгрузить();
	
	Если СтатусыОбработчиков.Найти(Перечисления.СтатусыОбработчиковОбновления.НеВыполнялся) <> Неопределено Тогда
		Возврат "СтатусНеВыполнено";
	ИначеЕсли СтатусыОбработчиков.Найти(Перечисления.СтатусыОбработчиковОбновления.Ошибка) <> Неопределено Тогда
		Возврат "СтатусОшибка";
	ИначеЕсли СтатусыОбработчиков.Найти(Перечисления.СтатусыОбработчиковОбновления.Приостановлен) <> Неопределено Тогда
		Возврат "СтатусПриостановлен";
	Иначе
		Возврат "";
	КонецЕсли;
	
КонецФункции

// Выполняет все процедуры отложенного обновления в цикле за один вызов.
//
Процедура ВыполнитьОтложенноеОбновлениеСейчас(ПараметрыОбновления = Неопределено) Экспорт
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	
	// Синхронное обновление, когда выполняется вызов из скрипта
	// при обновлении на очередное звено цепочки версий, требующей
	// выполнения обработчиков обновления перед переходом на следующую версию.
	ПараметрыЗапуска = СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере().Получить("ПараметрЗапуска");
	СинхронноеОбновление = ПараметрыОбновления <> Неопределено
		И (Не ПараметрыОбновления.ПриЗапускеКлиентскогоПриложения
			Или СтрНайти(ПараметрыЗапуска, "ВыполнитьОбновлениеИЗавершитьРаботу") > 0);
	
	Если СведенияОбОбновлении.ВремяОкончаниеОтложенногоОбновления <> Неопределено Тогда
		ВыполнитьДействияПослеОтложенногоОбновленияИнформационнойБазы(СинхронноеОбновление);
		Возврат;
	КонецЕсли;

	Если СведенияОбОбновлении.ВремяНачалаОтложенногоОбновления = Неопределено Тогда
		СведенияОбОбновлении.ВремяНачалаОтложенногоОбновления = ТекущаяДатаСеанса();
	КонецЕсли;
	
	Если ТипЗнч(СведенияОбОбновлении.НомерСеанса) <> Тип("СписокЗначений") Тогда
		СведенияОбОбновлении.НомерСеанса = Новый СписокЗначений;
	КонецЕсли;
	СведенияОбОбновлении.НомерСеанса.Добавить(НомерСеансаИнформационнойБазы());
	ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
	
	ОбработчикиВыполнялись = Истина;
	Обработанные = Новый Массив;
	Пока ОбработчикиВыполнялись Цикл
		ОбработчикиВыполнялись = ВыполнитьОтложенныйОбработчикОбновления(ПараметрыОбновления); // @skip-check query-in-loop - выполнение отложенных обработчиков.
		
		ОчередиДляОчистки = ОчередиДляОчистки(Обработанные); // @skip-check query-in-loop - получение актуальных данных об обработанных очередях.
		ОчиститьОбработанныеОчереди(ОчередиДляОчистки, Обработанные, СведенияОбОбновлении);
		
		Если ОбработчикиВыполнялись Тогда
			СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
			Если СведенияОбОбновлении.ОтложенноеОбновлениеЗавершеноУспешно <> Неопределено Тогда
				ОчиститьТранзакцииЗапускаОбработчиков();
				Прервать;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	ВыполнитьДействияПослеОтложенногоОбновленияИнформационнойБазы(СинхронноеОбновление);
	
КонецПроцедуры

// Для внутреннего использования.
Функция ДобавитьПараметрыРаботыКлиентаПриЗапуске(Параметры) Экспорт
	
	Параметры.Вставить("ВерсияДанныхОсновнойКонфигурации", ВерсияИБ(Метаданные.Имя));
	Если НеобходимоОбновлениеНеразделенныхДанныхИнформационнойБазы() Тогда
		Параметры.Вставить("НеобходимоОбновлениеНеразделенныхДанных");
	КонецЕсли;
	
	// Проверка продолжения работы.
	ЭтоВызовПередНачаломРаботыСистемы = Параметры.ПолученныеПараметрыКлиента <> Неопределено;
	УпрощеннаяФормаОбновленияИБ = Ложь;
	ОписаниеОшибки = ИнформационнаяБазаЗаблокированаДляОбновления(,
		ЭтоВызовПередНачаломРаботыСистемы, УпрощеннаяФормаОбновленияИБ);
	Если ЗначениеЗаполнено(ОписаниеОшибки) Тогда
		Параметры.Вставить("ИнформационнаяБазаЗаблокированаДляОбновления", ОписаниеОшибки);
		// Работа будет завершена.
		Возврат Ложь;
	КонецЕсли;
	
	Если УпрощеннаяФормаОбновленияИБ Тогда
		Параметры.Вставить("УпрощеннаяФормаОбновленияИБ");
	КонецЕсли;
	
	Если ТребуетсяПроверитьЛегальностьПолученияОбновления() Тогда
		Параметры.Вставить("ПроверитьЛегальностьПолученияОбновления");
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// АПК:581-выкл Используется при тестировании.
Функция ТребуетсяПроверитьЛегальностьПолученияОбновления() Экспорт
	
	Если НЕ ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ПроверкаЛегальностиПолученияОбновления") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если СтандартныеПодсистемыСервер.ЭтоБазоваяВерсияКонфигурации() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ЛегальнаяВерсия = "";
	
	Если РежимОбновленияДанныхВЛокальномРежимеРаботы() = "НачальноеЗаполнение" Тогда
		ЛегальнаяВерсия = Метаданные.Версия;
	Иначе
		СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
		ЛегальнаяВерсия = СведенияОбОбновлении.ЛегальнаяВерсия;
	КонецЕсли;
	
	Возврат ЛегальнаяВерсия <> Метаданные.Версия;
	
КонецФункции
// АПК:581-вкл

// Возвращает текст причины блокировки, если требуется обновление ИБ и у текущего пользователя
// для этого недостаточно прав, иначе возвращает пустую строку.
//
// Параметры:
//  УчитыватьПривилегированныйРежим - Булево - если указать Ложь, то при проверке прав текущего пользователя
//                                    наличие привилегированного режима не будет учитываться.
//  ПриЗапуске - Булево
//  УпрощеннаяФормаОбновленияИБ - Булево - возвращаемое значение.
//  
// Возвращаемое значение:
//  Строка - если база не заблокирована, тогда пустая строка, иначе сообщение о причине блокировки.
// 
Функция ИнформационнаяБазаЗаблокированаДляОбновления(УчитыватьПривилегированныйРежим = Истина,
			ПриЗапуске = Неопределено, УпрощеннаяФормаОбновленияИБ = Ложь) Экспорт
	
	Сообщение = "";
	
	ТекущийПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
	
	// Для входа в заблокированную базу достаточно только одного права администрирования.
	Если УчитыватьПривилегированныйРежим Тогда
		ЕстьПравоАдминистрирование = ПравоДоступа("Администрирование", Метаданные);
	Иначе
		ЕстьПравоАдминистрирование = ПравоДоступа("Администрирование", Метаданные, ТекущийПользовательИБ);
	КонецЕсли;
	
	СообщениеАдминистраторуСистемы =
		НСтр("ru = 'Вход в программу временно невозможен в связи с обновлением на новую версию.
		           |Для завершения обновления версии программы требуются административные права
		           |(роли ""Администратор системы"" и ""Полные права"").'");
	
	УстановитьПривилегированныйРежим(Истина);
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	ДоступноИспользованиеРазделенныхДанных = ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	УстановитьПривилегированныйРежим(Ложь);
	
	Если НеобходимоОбновлениеНеразделенныхДанныхИнформационнойБазы() Тогда
		
		СообщениеАдминистраторуОбластиДанных =
			НСтр("ru = 'Вход в приложение временно невозможен в связи с обновлением на новую версию.
			           |Обратитесь к администратору сервиса за подробностями.'");
		
		Если ДоступноИспользованиеРазделенныхДанных Тогда
			Сообщение = СообщениеАдминистраторуОбластиДанных;
			
		ИначеЕсли НЕ ЕстьПраваНаОбновлениеИнформационнойБазы(УчитыватьПривилегированныйРежим, Ложь) Тогда
			
			Если ЕстьПравоАдминистрирование Тогда
				Сообщение = СообщениеАдминистраторуСистемы;
			Иначе
				Сообщение = СообщениеАдминистраторуОбластиДанных;
			КонецЕсли;
		КонецЕсли;
		
		Возврат Сообщение;
	КонецЕсли;
	
	// Сообщение администратору сервиса не выдается.
	Если РазделениеВключено И Не ДоступноИспользованиеРазделенныхДанных Тогда
		Возврат "";
	КонецЕсли;
		
	Если ЕстьПраваНаОбновлениеИнформационнойБазы(УчитыватьПривилегированныйРежим,
			Истина, УпрощеннаяФормаОбновленияИБ) Тогда
		
		Если ОбновлениеИнформационнойБазы.НеобходимоОбновлениеИнформационнойБазы()
			И ПриЗапуске = Истина Тогда
			УстановитьПривилегированныйРежим(Истина);
			Результат = ОтметкаНачалаОбновления();
			УстановитьПривилегированныйРежим(Ложь);
			Если Не Результат.МожноВыполнятьОбновление Тогда
				Сообщение = НСтр("ru = 'Вход в программу временно невозможен в связи с обновлением на новую версию.
					|Обновление уже выполняется:
					|  компьютер - %1
					|  пользователь - %2
					|  сеанс - %3
					|  начат - %4
					|  приложение - %5'");
				
				Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Сообщение,
					Результат.СеансОбновления.ИмяКомпьютера,
					Результат.СеансОбновления.Пользователь,
					Результат.СеансОбновления.НомерСеанса,
					Результат.СеансОбновления.НачалоСеанса,
					Результат.СеансОбновления.ИмяПриложения);
				Возврат Сообщение;
			КонецЕсли;
		КонецЕсли;
		Возврат "";
	КонецЕсли;
	
	ТребуетсяПовторитьЗагрузкуСообщенияОбменаДаннымиПередЗапуском = Ложь;
	Если ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ()
	   И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		МодульОбменДаннымиСлужебный = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСлужебный");
		ТребуетсяПовторитьЗагрузкуСообщенияОбменаДаннымиПередЗапуском = 
			МодульОбменДаннымиСлужебный.ПовторитьЗагрузкуСообщенияОбменаДаннымиПередЗапуском();
	КонецЕсли;
	
	// В этих случаях запуск не блокируется.
	Если Не ОбновлениеИнформационнойБазы.НеобходимоОбновлениеИнформационнойБазы()
	   И Не ТребуетсяПроверитьЛегальностьПолученияОбновления()
	   И Не ТребуетсяПовторитьЗагрузкуСообщенияОбменаДаннымиПередЗапуском Тогда
		Возврат "";
	КонецЕсли;
	
	// Во всех остальных случаях запуск блокируется.
	Если ЕстьПравоАдминистрирование Тогда
		Возврат СообщениеАдминистраторуСистемы;
	КонецЕсли;

	Если РазделениеВключено Тогда
		// Сообщение пользователю сервиса.
		Сообщение =
			НСтр("ru = 'Вход в приложение временно невозможен в связи с обновлением на новую версию.
			           |Обратитесь к администратору сервиса за подробностями.'");
	Иначе
		// Сообщение пользователю локального режима.
		Сообщение =
			НСтр("ru = 'Вход в программу временно невозможен в связи с обновлением на новую версию.
			           |Обратитесь к администратору за подробностями.'");
	КонецЕсли;
	
	Возврат Сообщение;
	
КонецФункции

// Возвращаемое значение:
//  Булево
//
Функция УстановленоЗапуститьОбновлениеИнформационнойБазы() Экспорт
	
	Установлено = СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере(Ложь).Получить("ЗапуститьОбновлениеИнформационнойБазы");
	Возврат Установлено <> Неопределено;
	
КонецФункции

// Устанавливает состояние запуска обновления информационной.
// Требуется привилегированный режим.
//
// Параметры:
//  Запуск - Булево - если установить Истина, состояние будет установлено,
//           если установить Ложь, состояние будет снято.
//
Процедура УстановитьЗапускОбновленияИнформационнойБазы(Знач Запуск) Экспорт
	
	Если Не ЕстьПраваНаОбновлениеИнформационнойБазы(Ложь) Тогда
		Запуск = Ложь;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	ТекущиеПараметры = Новый Соответствие(ПараметрыСеанса.ПараметрыКлиентаНаСервере);
	
	Если Запуск = Истина Тогда
		ТекущиеПараметры.Вставить("ЗапуститьОбновлениеИнформационнойБазы", Истина);
		
	ИначеЕсли ТекущиеПараметры.Получить("ЗапуститьОбновлениеИнформационнойБазы") <> Неопределено Тогда
		ТекущиеПараметры.Удалить("ЗапуститьОбновлениеИнформационнойБазы");
	КонецЕсли;
	
	ПараметрыСеанса.ПараметрыКлиентаНаСервере = Новый ФиксированноеСоответствие(ТекущиеПараметры);
	
КонецПроцедуры

// Получает сведения об обновлении информационной базы
// из константы "СведенияОбОбновленииИБ".
// 
// Возвращаемое значение:
//  Структура:
//     * ВремяНачалаОбновления - Строка
//     * ВремяОкончанияОбновления - Строка
//     * ПродолжительностьОбновления - Строка
//     * ВремяНачалаОтложенногоОбновления - Строка
//     * ВремяОкончаниеОтложенногоОбновления - Строка
//     * НомерСеанса - СписокЗначений
//     * ПараметрыОбработчикаОбновления - Структура
//     * ОтложенноеОбновлениеЗавершеноУспешно - Строка
//     * ДеревоОбработчиков - ДеревоЗначений
//     * ВерсияДереваОбработчиков - Строка
//     * ВыводитьОписаниеОбновлений - Булево
//     * ЛегальнаяВерсия - Строка
//     * НовыеПодсистемы - Массив
//     * УправлениеОтложеннымОбновлением - Структура
//     * ОбрабатываемыеДанные - Соответствие
//     * ТекущаяИтерацияОбновления - Число
//     * ПланОтложенногоОбновления - Массив
//     * СеансОбновления - Структура
//     * ОписаниеПотоков - см. НовоеОписаниеПотоковРегистрацииДанныхОтложенногоОбновления
//     * ВерсияУдалениеПатчей - Строка
//     * ДлительностьЭтаповОбновления - Структура:
//         ** Критичные - Структура:
//              *** Начало - Дата
//              *** Конец  - Дата
//         ** Обычные - Структура:
//              *** Начало - Дата
//              *** Конец  - Дата
//         ** Некритичные - Структура:
//              *** Начало - Дата
//              *** Конец  - Дата
//
Функция СведенияОбОбновленииИнформационнойБазы() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ОбщегоНазначения.РазделениеВключено()
	   И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		
		Возврат НовыеСведенияОбОбновлении();
	КонецЕсли;
	
	СведенияОбОбновленииИБ = Константы.СведенияОбОбновленииИБ.Получить().Получить();
	Если ТипЗнч(СведенияОбОбновленииИБ) <> Тип("Структура") Тогда
		Возврат НовыеСведенияОбОбновлении();
	КонецЕсли;
	Если СведенияОбОбновленииИБ.Количество() = 1 Тогда
		Возврат НовыеСведенияОбОбновлении();
	КонецЕсли;
		
	СведенияОбОбновленииИБ = НовыеСведенияОбОбновлении(СведенияОбОбновленииИБ);
	Возврат СведенияОбОбновленииИБ;
	
КонецФункции

// Записывает данные по обновлению в константу "СведенияОбОбновленииИБ".
//
Процедура ЗаписатьСведенияОбОбновленииИнформационнойБазы(Знач СведенияОбОбновлении) Экспорт
	
	Если СведенияОбОбновлении = Неопределено Тогда
		НовоеЗначение = НовыеСведенияОбОбновлении();
	Иначе
		НовоеЗначение = СведенияОбОбновлении;
	КонецЕсли;
	
	МенеджерКонстанты = Константы.СведенияОбОбновленииИБ.СоздатьМенеджерЗначения();
	МенеджерКонстанты.Значение = Новый ХранилищеЗначения(НовоеЗначение);
	ОбновлениеИнформационнойБазы.ЗаписатьДанные(МенеджерКонстанты);
	
КонецПроцедуры

// Записывает в константу продолжительность основного цикла обновления.
//
Процедура ЗаписатьВремяВыполненияОбновления(ВремяНачалаОбновления, ВремяОкончанияОбновления) Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено() И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	СведенияОбОбновлении.ВремяНачалаОбновления = ВремяНачалаОбновления;
	СведенияОбОбновлении.ВремяОкончанияОбновления = ВремяОкончанияОбновления;
	
	ВремяВСекундах = ВремяОкончанияОбновления - ВремяНачалаОбновления;
	
	Часы = Цел(ВремяВСекундах/3600);
	Минуты = Цел((ВремяВСекундах - Часы * 3600) / 60);
	Секунды = ВремяВСекундах - Часы * 3600 - Минуты * 60;
	
	ПродолжительностьЧасы = ?(Часы = 0, "", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 час'"), Часы));
	ПродолжительностьМинуты = ?(Минуты = 0, "", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 мин'"), Минуты));
	ПродолжительностьСекунды = ?(Секунды = 0, "", СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 сек'"), Секунды));
	ПродолжительностьОбновления = ПродолжительностьЧасы + " " + ПродолжительностьМинуты + " " + ПродолжительностьСекунды;
	СведенияОбОбновлении.ПродолжительностьОбновления = СокрЛП(ПродолжительностьОбновления);
	
	ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура ЗаписатьПодтверждениеЛегальностиПолученияОбновлений() Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено()
	   И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных()
	   Или СтандартныеПодсистемыСервер.ЭтоБазоваяВерсияКонфигурации() Тогда
		
		Возврат;
	КонецЕсли;
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	СведенияОбОбновлении.ЛегальнаяВерсия = Метаданные.Версия;
	ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
	
КонецПроцедуры

// Устанавливает флаг отображения описаний изменений версий по
// текущую версию включительно, если для пользователя флаг не 
// был установлен ранее.
//
// Параметры:
//  ИмяПользователя - Строка - имя пользователя, для которого
//   необходимо установить флаг.
//
Процедура УстановитьФлагОтображенияОписанийДляНовогоПользователя(Знач ИмяПользователя) Экспорт
	
	Если ПоследняяВерсияОтображенияИзмененийСистемы(ИмяПользователя) = Неопределено Тогда
		УстановитьФлагОтображенияОписанийПоТекущуюВерсию(ИмяПользователя);
	КонецЕсли;
	
КонецПроцедуры

// Повторно регистрирует обновляемые данные на плане обмена
// ОбновлениеИнформационнойБазы, требуется при загрузке данных
// из сервиса в коробку и обратно.
//
Процедура ПеререгистрироватьДанныеДляОтложенногоОбновления() Экспорт
	
	ПараметрыИнициализированы = Ложь;
	
	Обработчики = ОбработчикиДляОтложеннойРегистрацииДанных();
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	ВерсииПодсистемНаНачалоОбновления = СведенияОбОбновлении.ВерсииПодсистемНаНачалоОбновления;
	
	Для Каждого Обработчик Из Обработчики Цикл
		ВерсияПодсистемыНаНачалоОбновления = ВерсииПодсистемНаНачалоОбновления[Обработчик.ИмяБиблиотеки];
		
		Если Не ПараметрыИнициализированы Тогда
			ПараметрыОбработчикаСтруктура = ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке();
			ПараметрыОбработчикаСтруктура.ПовторнаяРегистрация = Истина;
			ПараметрыИнициализированы = Истина;
		КонецЕсли;
		
		ПараметрыОбработчикаСтруктура.Очередь = Обработчик.ОчередьОтложеннойОбработки;
		ПараметрыОбработчикаСтруктура.ИмяОбработчика = Обработчик.ИмяОбработчика;
		ПараметрыОбработчикаСтруктура.Вставить("ДанныеОбработчика", Новый Соответствие);
		ПараметрыОбработчикаСтруктура.Вставить("АктуальныеДанные", ОбновлениеИнформационнойБазы.ПараметрыВыборкиАктуальныхДанных());
		ПараметрыОбработчикаСтруктура.Вставить("ЗарегистрированныеТаблицыРегистраторов", Новый Соответствие);
		ПараметрыОбработчикаСтруктура.Вставить("ВерсияПодсистемыНаНачалоОбновления", ВерсияПодсистемыНаНачалоОбновления);
		
		Если Обработчик.Многопоточный Тогда
			ПараметрыОбработчикаСтруктура.ПараметрыВыборки =
				ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки();
		Иначе
			ПараметрыОбработчикаСтруктура.ПараметрыВыборки = Неопределено;
		КонецЕсли;
		
		ПараметрыОбработчика = Новый Массив;
		ПараметрыОбработчика.Добавить(ПараметрыОбработчикаСтруктура);
		Попытка
			Сообщение = НСтр("ru = 'Выполняется процедура заполнения данных
				                   |""%1""
				                   |отложенного обработчика обновления
				                   |""%2"".'");
			Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Сообщение,
				Обработчик.ПроцедураЗаполненияДанныхОбновления,
				Обработчик.ИмяОбработчика);
			ЗаписатьИнформацию(Сообщение);
			
			ОбщегоНазначения.ВыполнитьМетодКонфигурации(Обработчик.ПроцедураЗаполненияДанныхОбновления, ПараметрыОбработчика);
			Если Обработчик.Многопоточный Тогда
				ИсправитьПолныеИменаВПараметрахВыборки(ПараметрыОбработчикаСтруктура.ПараметрыВыборки);
			КонецЕсли;
		Исключение
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'При вызове процедуры заполнения данных
						   |""%1""
						   |отложенного обработчика обновления
						   |""%2""
						   |произошла ошибка:
						   |""%3"".'"),
				Обработчик.ПроцедураЗаполненияДанныхОбновления,
				Обработчик.ИмяОбработчика,
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			ЗаписатьОшибку(ТекстОшибки);
			
			Свойства = Новый Структура;
			Свойства.Вставить("Статус", Перечисления.СтатусыОбработчиковОбновления.Ошибка);
			Свойства.Вставить("ИнформацияОбОшибке", ТекстОшибки);
			УстановитьСвойстваОбработчика(Обработчик.ИмяОбработчика, Свойства);

			ВызватьИсключение;
		КонецПопытки;
		
		ОписаниеОбрабатываемыхДанных = НовоеОписаниеОбрабатываемыхДанных(Обработчик.Многопоточный);
		ОписаниеОбрабатываемыхДанных.ДанныеОбработчика = ПараметрыОбработчикаСтруктура.ДанныеОбработчика;
		ОписаниеОбрабатываемыхДанных.ИмяОбработчика    = Обработчик.ИмяОбработчика;
		ОписаниеОбрабатываемыхДанных.АктуальныеДанные  = ПараметрыОбработчикаСтруктура.АктуальныеДанные;
		Если ЗначениеЗаполнено(ПараметрыОбработчикаСтруктура.ЗарегистрированныеТаблицыРегистраторов) Тогда
			ЗарегистрированныеТаблицы = Новый Массив;
			Для Каждого КлючИЗначение Из ПараметрыОбработчикаСтруктура.ЗарегистрированныеТаблицыРегистраторов Цикл
				ЗарегистрированныеТаблицы.Добавить(КлючИЗначение.Значение);
			КонецЦикла;
		КонецЕсли;
		ОписаниеОбрабатываемыхДанных.ЗарегистрированныеТаблицыРегистраторов = ЗарегистрированныеТаблицы;
		
		Если Обработчик.Многопоточный Тогда
			ОписаниеОбрабатываемыхДанных.ПараметрыВыборки = ПараметрыОбработчикаСтруктура.ПараметрыВыборки;
		КонецЕсли;
		
		ОписаниеОбрабатываемыхДанных = Новый ХранилищеЗначения(ОписаниеОбрабатываемыхДанных, Новый СжатиеДанных(9));
		УстановитьСвойствоОбработчика(Обработчик.ИмяОбработчика, "ОбрабатываемыеДанные", ОписаниеОбрабатываемыхДанных);
		
	КонецЦикла;
	
КонецПроцедуры

// Возвращает параметры отложенного обработчика обновления.
// Проверяет, есть ли сохраненные параметры у обработчика обновления
// и возвращает их.
// 
// Параметры:
//  Идентификатор - Строка
//                - УникальныйИдентификатор - имя процедуры обработчика или
//                  его уникальный идентификатор.
//
// Возвращаемое значение:
//  Структура - сохраненные параметры обработчика обновления.
//
Функция ПараметрыОтложенногоОбработчикаОбновления(Идентификатор) Экспорт
	ОбработчикОбновления = ОбработчикОбновления(Идентификатор);
	
	Если ОбработчикОбновления = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	СтатистикаВыполнения = ОбработчикОбновления.СтатистикаВыполнения.Получить();
	Параметры = СтатистикаВыполнения["ПараметрыОбработчика"];
	Если Параметры = Неопределено Тогда
		Параметры = Новый Структура;
	КонецЕсли;
	
	Возврат Параметры;
КонецФункции

// Записывает параметры отложенного обработчика обновления
// 
// Параметры:
//  Идентификатор - Строка
//                - УникальныйИдентификатор - имя процедуры обработчика или
//                  его уникальный идентификатор.
//  Параметры     - Структура - параметры, которые необходимо сохранить.
//
Процедура ЗаписатьПараметрыОтложенногоОбработчикаОбновления(Идентификатор, Параметры) Экспорт
	ОбработчикОбновления = ОбработчикОбновления(Идентификатор);
	
	Если ОбработчикОбновления = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	СтатистикаВыполнения = ОбработчикОбновления.СтатистикаВыполнения.Получить();
	СтатистикаВыполнения.Вставить("ПараметрыОбработчика", Параметры);
	
	УстановитьСвойствоОбработчика(Идентификатор,
		"СтатистикаВыполнения",
		Новый ХранилищеЗначения(СтатистикаВыполнения));
КонецПроцедуры

// Возвращает количество потоков обновления информационной базы.
//
// Если количество указано в параметре запуска "ЧислоПотоковОбновления", то возвращается оно.
// Иначе, если значение константы КоличествоПотоковОбновленияИнформационнойБазы установлено, то возвращается оно.
// Иначе возвращается значение по умолчанию, см. КоличествоПотоковОбновленияИнформационнойБазыПоУмолчанию.
//
// Возвращаемое значение:
//  Число - количество потоков.
//
Функция КоличествоПотоковОбновленияИнформационнойБазы() Экспорт
	
	Если РазрешеноМногопоточноеОбновление() Тогда
		Количество = 0;
		ИмяПараметра = "ЧислоПотоковОбновления=";
		Параметры = СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере().Получить("ПараметрЗапуска");
		ПозицияПараметра = СтрНайти(Параметры, ИмяПараметра);
		
		Если ПозицияПараметра > 0 Тогда
			ПозицияРазделителя = СтрНайти(Параметры, ";",, ПозицияПараметра + СтрДлина(ИмяПараметра));
			Длина = ?(ПозицияРазделителя > 0, ПозицияРазделителя, СтрДлина(Параметры) + 1) - ПозицияПараметра;
			ПотокиОбновления = СтрРазделить(Сред(Параметры, ПозицияПараметра, Длина), "=");
			
			Попытка
				Количество = Число(ПотокиОбновления[1]);
			Исключение
				ТекстИсключения = НСтр(
					"ru = 'Укажите параметр запуска программы ""%1"" в формате
					|""%1=Х"", где ""Х"" - максимальное количество потоков обновления.'");
				ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, "ЧислоПотоковОбновления");
				ВызватьИсключение ТекстИсключения;
			КонецПопытки;
		КонецЕсли;
		
		Если Количество = 0 Тогда
			Количество = Константы.КоличествоПотоковОбновленияИнформационнойБазы.Получить();
			
			Если Количество = 0 Тогда
				Количество = КоличествоПотоковОбновленияИнформационнойБазыПоУмолчанию();
			КонецЕсли;
		КонецЕсли;
		
		Возврат Количество;
	Иначе
		Возврат 1;
	КонецЕсли;
	
КонецФункции

// Возвращает итерации обновления.
//
// Возвращаемое значение:
//  Массив из см. ИтерацияОбновления
//
Функция ИтерацииОбновления() Экспорт
	
	ИмяОсновнойКонфигурации = Метаданные.Имя;
	ИтерацияОбновлениеОсновнойПодсистемы = Неопределено;
	
	ИтерацииОбновления = Новый Массив;
	ОписанияПодсистем  = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем();
	ВерсииПодсистем    = ВерсияИБ(Неопределено);
	Для каждого ИмяПодсистемы Из ОписанияПодсистем.Порядок Цикл
		ОписаниеПодсистемы = ОписанияПодсистем.ПоИменам.Получить(ИмяПодсистемы);
		Если НЕ ЗначениеЗаполнено(ОписаниеПодсистемы.ОсновнойСерверныйМодуль) Тогда
			Продолжить;
		КонецЕсли;
		Модуль = ОбщегоНазначения.ОбщийМодуль(ОписаниеПодсистемы.ОсновнойСерверныйМодуль);
		
		ИтерацияОбновления = ИтерацияОбновления(ОписаниеПодсистемы.Имя, ОписаниеПодсистемы.Версия, 
			ОбновлениеИнформационнойБазы.НоваяТаблицаОбработчиковОбновления(), ОписаниеПодсистемы.Имя = ИмяОсновнойКонфигурации);
		ИтерацияОбновления.ИмяОсновногоСерверногоМодуля = ОписаниеПодсистемы.ОсновнойСерверныйМодуль;
		ИтерацияОбновления.ОсновнойСерверныйМодуль = Модуль;
		Если Не ЗначениеЗаполнено(ВерсииПодсистем) Тогда
			ИтерацияОбновления.ПредыдущаяВерсия = "0.0.0.0";
		Иначе
			ПредыдущаяВерсия = ВерсииПодсистем[ОписаниеПодсистемы.Имя];
			Если ПредыдущаяВерсия = Неопределено Тогда
				ИтерацияОбновления.ПредыдущаяВерсия = "0.0.0.0";
			Иначе
				ИтерацияОбновления.ПредыдущаяВерсия = ВерсииПодсистем[ОписаниеПодсистемы.Имя];
			КонецЕсли;
		КонецЕсли;
		ИтерацииОбновления.Добавить(ИтерацияОбновления);
		
		Модуль.ПриДобавленииОбработчиковОбновления(ИтерацияОбновления.Обработчики);
		
		Если ОписаниеПодсистемы.Имя = ИмяОсновнойКонфигурации Тогда
			ИтерацияОбновлениеОсновнойПодсистемы = ИтерацияОбновления;
		КонецЕсли;
		
		ПроверитьСвойстваОбработчиков(ИтерацияОбновления);
	КонецЦикла;
	
	Если ИтерацияОбновлениеОсновнойПодсистемы = Неопределено И ИмяОсновнойКонфигурации = "БиблиотекаСтандартныхПодсистем" Тогда
		ТекстСообщения = НСтр("ru = 'Файл поставки 1С:Библиотека стандартных подсистем не предназначен для создания информационных баз по шаблону. 
			|Перед использованием необходимо ознакомиться с документацией на ИТС (https://its.1c.eu/db/bspdoc)'");
		ВызватьИсключение ТекстСообщения;
	КонецЕсли;
	
	Возврат ИтерацииОбновления;
	
КонецФункции

// АПК:581-выкл Для механизма обновления ИБ в копии.

// Обновляет список выполняемых обработчиков обновления в регистрах сведений
// ОбработчикиОбновления и ОбработчикиОбновленияОбщихДанных.
//
Процедура ОбновитьСписокВыполняемыхОбработчиковОбновления(ИтерацииОбновления, ПервыйОбменВРИБ = Ложь, ТипыОбработчиков = Неопределено) Экспорт
	
	ТолькоОтложенные  = (ТипыОбработчиков = "Отложенные");
	ТолькоМонопольные = (ТипыОбработчиков = "Монопольные");
	
	Если Не ТолькоМонопольные Тогда
		СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
		НезавершенныеОтложенныеОбработчики = НезавершенныеОтложенныеОбработчики(СведенияОбОбновлении);
	КонецЕсли;
	НачатьТранзакцию();
	Попытка
		
		Если Не ТолькоМонопольные Тогда
			ВерсииПодсистемНаНачалоОбновления = Новый Соответствие;
			ОтложенноеОбновлениеЗавершеноУспешно = Неопределено;
			ЗаполнитьВерсииПодсистем = Ложь;
			Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
				ОтложенноеОбновлениеЗавершеноУспешно = Константы.ОтложенноеОбновлениеЗавершеноУспешно.Получить();
				ВерсииПодсистемНаНачалоОбновления    = СведенияОбОбновлении.ВерсииПодсистемНаНачалоОбновления;
				ЗаполнитьВерсииПодсистем = Истина;
			КонецЕсли;
			
			Если ОтложенноеОбновлениеЗавершеноУспешно = Истина Тогда
				ВерсииПодсистемНаНачалоОбновления.Очистить();
			КонецЕсли;
			
			СброситьСведенияОбОбновлении(СведенияОбОбновлении);
			
			ПроверитьУникальностьИдентификаторовОтложенныхОбработчиков(ИтерацииОбновления);
		КонецЕсли;
		
		// АПК:1327-выкл нет конкурентной работы с регистром
		Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
			НаборРазделенныхОбработчиков = РегистрыСведений.ОбработчикиОбновления.СоздатьНаборЗаписей();
			Если ТолькоМонопольные Тогда
				// Остаются только отложенные обработчики, монопольные и оперативные добавляются заново.
				НаборРазделенныхОбработчиков.Прочитать();
				ТаблицаОбработчиков = НаборРазделенныхОбработчиков.Выгрузить();
				
				ПараметрыОтбора = Новый Структура;
				ПараметрыОтбора.Вставить("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
				ТаблицаОбработчиков = ТаблицаОбработчиков.Скопировать(ПараметрыОтбора);
				
				НаборРазделенныхОбработчиков.Загрузить(ТаблицаОбработчиков);
			ИначеЕсли ТолькоОтложенные Тогда
				// Монопольные и оперативные обработчики остаются, т.к. повторно регистрируются только отложенные.
				НаборРазделенныхОбработчиков.Прочитать();
				ТаблицаОбработчиков = НаборРазделенныхОбработчиков.Выгрузить();
				
				ПараметрыОтбора = Новый Структура;
				ПараметрыОтбора.Вставить("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Монопольно);
				МонопольныеОбработчики = ТаблицаОбработчиков.Скопировать(ПараметрыОтбора);
				
				ПараметрыОтбора.Вставить("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Оперативно);
				ОперативныеОбработчики = ТаблицаОбработчиков.Скопировать(ПараметрыОтбора);
				
				ОбщегоНазначенияКлиентСервер.ДополнитьТаблицу(МонопольныеОбработчики, ОперативныеОбработчики);
				
				НаборРазделенныхОбработчиков.Загрузить(ОперативныеОбработчики);
			КонецЕсли;
			НаборРазделенныхОбработчиков.Записать();
		КонецЕсли;
		Если Не ТолькоОтложенные И Не ОбщегоНазначения.РазделениеВключено() Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
			НаборНеразделенныхОбработчиков = РегистрыСведений.ОбработчикиОбновленияОбщихДанных.СоздатьНаборЗаписей();
			НаборНеразделенныхОбработчиков.Записать();
		КонецЕсли;
		// АПК:1327-вкл
		
		ОписанияБиблиотек = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем().ПоИменам;
		ГруппаОбновления = 1;
		ТекущийРежимВыполнения = "";
		ТекстОшибок = "";
		ДобавленныеОбработчики = Новый Массив;
		Для Каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
			
			Если Не ТолькоМонопольные И ЗаполнитьВерсииПодсистем Тогда
				СохраненнаяВерсия = ВерсииПодсистемНаНачалоОбновления[ИтерацияОбновления.Подсистема];
				Если СохраненнаяВерсия = Неопределено Тогда
					ВерсииПодсистемНаНачалоОбновления.Вставить(ИтерацияОбновления.Подсистема, ИтерацияОбновления.ПредыдущаяВерсия);
				Иначе
					Если ОбщегоНазначенияКлиентСервер.СравнитьВерсии(СохраненнаяВерсия, ИтерацияОбновления.ПредыдущаяВерсия) > 0 Тогда
						ВерсииПодсистемНаНачалоОбновления.Вставить(ИтерацияОбновления.Подсистема, ИтерацияОбновления.ПредыдущаяВерсия);
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
			
			// Добавление монопольных и оперативных обработчиков обновления.
			ИмяБиблиотеки = ИтерацияОбновления.Подсистема;
			Если Не ТолькоОтложенные Тогда
				Если Не ПервыйОбменВРИБ Тогда
					ПредыдущаяВерсия = ИтерацияОбновления.ПредыдущаяВерсия;
					ВерсияМетаданных = ИтерацияОбновления.Версия;
					Если ПустаяСтрока(ВерсияМетаданных) Тогда
						ВерсияМетаданных = "0.0.0.0";
					КонецЕсли;
					
					ОбработчикиПоВерсиям = Неопределено;
					Если ПредыдущаяВерсия <> "0.0.0.0"
						И ОбщегоНазначения.РазделениеВключено()
						И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
						ОбработчикиПоВерсиям = ПолучитьПланОбновления(ИтерацияОбновления.Подсистема, ПредыдущаяВерсия, ВерсияМетаданных);
					КонецЕсли;
					
					Если ОбработчикиПоВерсиям = Неопределено Тогда
						ОбработчикиПоВерсиям = ОбработчикиОбновленияВИнтервале(ИтерацияОбновления.Обработчики,
							ПредыдущаяВерсия, ИтерацияОбновления.Версия);
					КонецЕсли;
					
					Если ОбработчикиПоВерсиям.Строки.Количество() <> 0 Тогда
						ОбработчикиПоВерсиям.Колонки.Процедура.Имя = "ИмяОбработчика";
						ДобавитьОбработчики(ИмяБиблиотеки, ОбработчикиПоВерсиям.Строки, ДобавленныеОбработчики);
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
			
			Если ТолькоМонопольные Тогда
				Продолжить;
			КонецЕсли;
			
			// Добавление отложенных обработчиков обновления.
			Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
				// Обработка сценария получения первого сообщения обмена от подчиненного узла после обновления.
				ПредыдущаяВерсия = ?(ПервыйОбменВРИБ, "1.0.0.0", ИтерацияОбновления.ПредыдущаяВерсия);
				РежимВыполненияОтложенныхОбработчиков = ОписанияБиблиотек[ИмяБиблиотеки].РежимВыполненияОтложенныхОбработчиков;
				ПараллельноСВерсии = ОписанияБиблиотек[ИмяБиблиотеки].ПараллельноеОтложенноеОбновлениеСВерсии;
				Если ПервыйОбменВРИБ И РежимВыполненияОтложенныхОбработчиков = "Последовательно" Тогда
					Продолжить;
				КонецЕсли;
				
				ПараметрыОтбора = ПараметрыОтбораОбработчиков();
				ПараметрыОтбора.ПолучатьРазделенные = Истина;
				ПараметрыОтбора.РежимОбновления = "Отложенно";
				ПараметрыОтбора.УчитыватьПервыйОбменВРИБ = (РежимВыполненияОтложенныхОбработчиков = "Параллельно");
				ПараметрыОтбора.ПервыйОбменВРИБ = ПервыйОбменВРИБ;
				
				ОбработчикиПоВерсиям = ОбработчикиОбновленияВИнтервале(ИтерацияОбновления.Обработчики,
					ПредыдущаяВерсия, ИтерацияОбновления.Версия, ПараметрыОтбора);
				ОбработчикиПоВерсиям.Колонки.Процедура.Имя = "ИмяОбработчика";
				ДобавитьНезавершенныеОтложенныеОбработчики(
					ИтерацияОбновления,
					ИмяБиблиотеки,
					НезавершенныеОтложенныеОбработчики,
					ОбработчикиПоВерсиям.Строки);
				
				Если ПервыйОбменВРИБ Тогда
					УдаляемыеСтроки = Новый Массив;
					Для Каждого СтрокаВерсия Из ОбработчикиПоВерсиям.Строки Цикл
						Если СтрокаВерсия.Версия = "*"
							Или (ЗначениеЗаполнено(ПараллельноСВерсии)
								И ОбщегоНазначенияКлиентСервер.СравнитьВерсии(СтрокаВерсия.Версия, ПараллельноСВерсии) < 0) Тогда
							УдаляемыеСтроки.Добавить(СтрокаВерсия);
						КонецЕсли;
					КонецЦикла;
					
					Для Каждого УдаляемаяСтрока Из УдаляемыеСтроки Цикл
						ОбработчикиПоВерсиям.Строки.Удалить(УдаляемаяСтрока);
					КонецЦикла;
				КонецЕсли;
				
				Если ОбработчикиПоВерсиям.Строки.Количество() = 0 Тогда
					Продолжить;
				КонецЕсли;
				
				УпорядочитьВерсииОбработчиков(ОбработчикиПоВерсиям);
				
				ДобавитьОтложенныеОбработчики(ИмяБиблиотеки, ОбработчикиПоВерсиям.Строки, ГруппаОбновления, ТекстОшибок, ТекущийРежимВыполнения);
			КонецЕсли;
			
		КонецЦикла;
		
		Если ЗначениеЗаполнено(ТекстОшибок) Тогда
			ВызватьИсключение ТекстОшибок;
		КонецЕсли;
		
		Если (ТипыОбработчиков = Неопределено Или ТолькоОтложенные)
			И (Не ОбщегоНазначения.РазделениеВключено() Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных()) Тогда
			Константы.ПорядокОбрабатываемыхДанных.Установить(Перечисления.ПорядокОбработчиковОбновления.Критичный);
		КонецЕсли;
		
		Если Не ТолькоМонопольные И ЗаполнитьВерсииПодсистем Тогда
			СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
			СведенияОбОбновлении.ВерсииПодсистемНаНачалоОбновления = ВерсииПодсистемНаНачалоОбновления;
			ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// АПК:581-вкл

// Вызывается для включения и отключении отложенного обновления.
//
// Параметры:
//   Использование - Булево - Истина, если задание нужно включить, иначе Ложь.
//
Процедура ПриВключенииОтложенногоОбновления(Использование) Экспорт
	
	ОтборЗаданий = Новый Структура;
	ОтборЗаданий.Вставить("Метаданные", Метаданные.РегламентныеЗадания.ОтложенноеОбновлениеИБ);
	Задания = РегламентныеЗаданияСервер.НайтиЗадания(ОтборЗаданий);
	
	Для Каждого Задание Из Задания Цикл
		Если Задание.Использование = Использование Тогда
			Продолжить;
		КонецЕсли;
		
		ПараметрыЗадания = Новый Структура("Использование", Использование);
		
		ТребуетсяУстановитьЗапланированныйМоментЗапуска = Ложь;
		Если ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
			МодульТехнологияСервиса = ОбщегоНазначения.ОбщийМодуль("ТехнологияСервиса");
			ВерсияБиблиотекиБТС = МодульТехнологияСервиса.ВерсияБиблиотеки();
			ТребуетсяУстановитьЗапланированныйМоментЗапуска = ОбщегоНазначенияКлиентСервер.СравнитьВерсии(ВерсияБиблиотекиБТС, "2.0.1.0") > 0;
		КонецЕсли;
		
		Если ОбщегоНазначения.РазделениеВключено()
			И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса")
			И Использование
			И ТребуетсяУстановитьЗапланированныйМоментЗапуска Тогда
			МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
			ЗапланированныйМоментЗапуска = МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ЗапланированныйМоментЗапускаОбновленияОбласти();
			ПараметрыЗадания.Вставить("ЗапланированныйМоментЗапуска", ЗапланированныйМоментЗапуска);
		КонецЕсли;
		
		РегламентныеЗаданияСервер.ИзменитьЗадание(Задание.УникальныйИдентификатор, ПараметрыЗадания);
	КонецЦикла;
	
КонецПроцедуры

// Выполняет оперативные и монопольные обработчики обновления
// по переданной итерации обновления.
//
Функция ВыполнитьИтерациюОбновления(Знач ИтерацияОбновления, Знач Параметры) Экспорт
	
	ИдентификаторБиблиотеки = ИтерацияОбновления.Подсистема;
	ВерсияМетаданныхИБ      = ИтерацияОбновления.Версия;
	ОбработчикиОбновления   = ИтерацияОбновления.Обработчики;
	
	ТекущаяВерсияИБ = ИтерацияОбновления.ПредыдущаяВерсия;
	
	НоваяВерсияИБ = ТекущаяВерсияИБ;
	ВерсияМетаданных = ВерсияМетаданныхИБ;
	Если ПустаяСтрока(ВерсияМетаданных) Тогда
		ВерсияМетаданных = "0.0.0.0";
	КонецЕсли;
	
	Если ТекущаяВерсияИБ <> "0.0.0.0"
		И ОбщегоНазначения.РазделениеВключено()
		И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		
		// Получение списка обработчиков сформированного на этапе выполнения неразделенных обработчиков.
		ВыполняемыеОбработчики = ПолучитьПланОбновления(ИдентификаторБиблиотеки, ТекущаяВерсияИБ, ВерсияМетаданных);
		Если ВыполняемыеОбработчики = Неопределено Тогда
			Если ИтерацияОбновления.ЭтоОсновнаяКонфигурация Тогда 
				ШаблонСообщения = НСтр("ru = 'Не существует план обновления конфигурации %1 с версии %2 на версию %3'");
			Иначе
				ШаблонСообщения = НСтр("ru = 'Не существует план обновления библиотеки %1 с версии %2 на версию %3'");
			КонецЕсли;
			Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения, ИдентификаторБиблиотеки, ТекущаяВерсияИБ, ВерсияМетаданных);
			ЗаписатьИнформацию(Сообщение);
			
			ВыполняемыеОбработчики = ОбработчикиОбновленияВИнтервале(ОбработчикиОбновления, ТекущаяВерсияИБ, ВерсияМетаданных);
		КонецЕсли;
	Иначе
		ВыполняемыеОбработчики = ОбработчикиОбновленияВИнтервале(ОбработчикиОбновления, ТекущаяВерсияИБ, ВерсияМетаданных);
	КонецЕсли;
	
	ОбязательныеРазделенныеОбработчики = ОбновлениеИнформационнойБазы.НоваяТаблицаОбработчиковОбновления();
	ИсходнаяВерсияИБ = ТекущаяВерсияИБ;
	ЗаписыватьВЖурнал = Константы.ДетализироватьОбновлениеИБВЖурналеРегистрации.Получить();
	
	Для Каждого Версия Из ВыполняемыеОбработчики.Строки Цикл
		
		Если Версия.Версия = "*" Тогда
			Сообщение = НСтр("ru = 'Выполняются обязательные процедуры обновления информационной базы.'");
		Иначе
			НоваяВерсияИБ = Версия.Версия;
			Если ТекущаяВерсияИБ = "0.0.0.0" Тогда
				Сообщение = НСтр("ru = 'Выполняется начальное заполнение данных.'");
			ИначеЕсли ИтерацияОбновления.ЭтоОсновнаяКонфигурация Тогда 
				Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Выполняется обновление информационной базы с версии %1 на версию %2.'"), 
					ТекущаяВерсияИБ, НоваяВерсияИБ);
			Иначе
				Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Выполняется обновление данных библиотеки %3 с версии %1 на версию %2.'"), 
					ТекущаяВерсияИБ, НоваяВерсияИБ, ИдентификаторБиблиотеки);
			КонецЕсли;
		КонецЕсли;
		ЗаписатьИнформацию(Сообщение);
		
		Для Каждого Обработчик Из Версия.Строки Цикл
			
			ПараметрыОбработчика = Неопределено;
			Если Обработчик.ВерсияРегистрации = "*" Тогда
				
				Если Обработчик.УправлениеОбработчиками Тогда
					ПараметрыОбработчика = Новый Структура;
					ПараметрыОбработчика.Вставить("РазделенныеОбработчики", ОбязательныеРазделенныеОбработчики);
				КонецЕсли;
				
				Если Обработчик.МонопольныйРежим = Истина Или Обработчик.РежимВыполнения = "Монопольно" Тогда
					Если Параметры.ОперативноеОбновление Тогда
						// Проверки выполняются в ВозможноОперативноеОбновление, а само обновление для таких
						// обработчиков выполняется только при неоперативном обновлении.
						Продолжить;
					КонецЕсли;
					
					Если ПараметрыОбработчика = Неопределено Тогда
						ПараметрыОбработчика = Новый Структура;
					КонецЕсли;
					ПараметрыОбработчика.Вставить("МонопольныйРежим", Истина);
				КонецЕсли;
			КонецЕсли;
			
			ДополнительныеПараметры = Новый Структура;
			ДополнительныеПараметры.Вставить("ЗаписыватьВЖурнал", ЗаписыватьВЖурнал);
			ДополнительныеПараметры.Вставить("ИдентификаторБиблиотеки", ИдентификаторБиблиотеки);
			ДополнительныеПараметры.Вставить("ХодВыполненияОбработчиков", Параметры.ХодВыполненияОбработчиков);
			ДополнительныеПараметры.Вставить("ВФоне", Параметры.ВФоне);
			
			ВыполнитьОбработчикОбновления(Обработчик, ПараметрыОбработчика, ДополнительныеПараметры);
		КонецЦикла;
		
		Если Версия.Версия = "*" Тогда
			Сообщение = НСтр("ru = 'Выполнены обязательные процедуры обновления информационной базы.'");
		ИначеЕсли СтрНачинаетсяС(Версия.Версия, "ОтладкаОбработчика") Тогда
			Сообщение = НСтр("ru = 'Выполнены отлаживаемые процедуры обновления информационной базы.'");
		Иначе
			Если ИтерацияОбновления.ЭтоОсновнаяКонфигурация Тогда 
				Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Выполнено обновление информационной базы с версии %1 на версию %2.'"), 
					ТекущаяВерсияИБ, НоваяВерсияИБ);
			Иначе
				Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Выполнено обновление данных библиотеки %3 с версии %1 на версию %2.'"), 
					ТекущаяВерсияИБ, НоваяВерсияИБ, ИдентификаторБиблиотеки);
			КонецЕсли;
		КонецЕсли;
		ЗаписатьИнформацию(Сообщение);
		
		Если Версия.Версия <> "*" И Не СтрНачинаетсяС(Версия.Версия, "ОтладкаОбработчика") Тогда
			// Установка номера версии информационной базы.
			УстановитьВерсиюИБ(ИдентификаторБиблиотеки, НоваяВерсияИБ, ИтерацияОбновления.ЭтоОсновнаяКонфигурация);
			ТекущаяВерсияИБ = НоваяВерсияИБ;
		КонецЕсли;
		
	КонецЦикла;
	
	// Установка номера версии информационной базы.
	ОтметитьРегистрациюДанных = Неопределено;
	Если Параметры.Свойство("ОтметитьРегистрациюДанных") Тогда
		ОтметитьРегистрациюДанных = Параметры.ОтметитьРегистрациюДанных;
	КонецЕсли;
	Если ВерсияИБ(ИдентификаторБиблиотеки) <> ВерсияМетаданныхИБ Тогда
		УстановитьВерсиюИБ(ИдентификаторБиблиотеки, ВерсияМетаданныхИБ, ИтерацияОбновления.ЭтоОсновнаяКонфигурация, ОтметитьРегистрациюДанных);
	КонецЕсли;
	
	Если ТекущаяВерсияИБ <> "0.0.0.0" Тогда
		
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
			
			МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
			МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.СформироватьПланОбновленияОбластиДанных(ИдентификаторБиблиотеки, ОбработчикиОбновления,
				ОбязательныеРазделенныеОбработчики, ИсходнаяВерсияИБ, ВерсияМетаданныхИБ);
			
		КонецЕсли;
		
	КонецЕсли;
	
	Возврат ВыполняемыеОбработчики;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Обработчики событий подсистем конфигурации.

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииОбработчиковУстановкиПараметровСеанса.
Процедура ПриДобавленииОбработчиковУстановкиПараметровСеанса(Обработчики) Экспорт
	
	Обработчики.Вставить("ВыполняетсяОбновлениеИБ", "ОбновлениеИнформационнойБазыСлужебный.УстановкаПараметровСеанса");
	Обработчики.Вставить("ПараметрыОбработчикаОбновления", "ОбновлениеИнформационнойБазыСлужебный.УстановкаПараметровСеанса");
	Обработчики.Вставить("ДлительныеОперации", "ДлительныеОперации.УстановкаПараметровСеанса");
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииИсключенийПоискаСсылок.
Процедура ПриДобавленииИсключенийПоискаСсылок(ИсключенияПоискаСсылок) Экспорт
	
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ДанныеОбработанныеВЦентральномУзлеРИБ.ПолноеИмя());

КонецПроцедуры

// См. ЦентрМониторингаПереопределяемый.ПриСбореПоказателейСтатистикиКонфигурации.
Процедура ПриСбореПоказателейСтатистикиКонфигурации() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЦентрМониторинга") Тогда
		Возврат;
	КонецЕсли;
	
	МодульЦентрМониторинга = ОбщегоНазначения.ОбщийМодуль("ЦентрМониторинга");
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	Если СведенияОбОбновлении.ОтложенноеОбновлениеЗавершеноУспешно <> Истина Тогда
		Возврат; // Получаем сведения только в том случае, когда отложенное обновление успешно завершилось.
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ОбработчикиОбновления.ДлительностьОбработки КАК ДлительностьОбработки
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения";
	Обработчики = Запрос.Выполнить().Выгрузить();
	
	Для Каждого Обработчик Из Обработчики Цикл
		МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации("ВремяВыполненияОтложенногоОбработчика." + Обработчик.ИмяОбработчика, Обработчик.ДлительностьОбработки / 1000);
	КонецЦикла;
	
	ВремяНачала = СведенияОбОбновлении.ВремяНачалаОбновления;
	ВремяОкончания = СведенияОбОбновлении.ВремяОкончанияОбновления;
	
	Если ЗначениеЗаполнено(ВремяНачала) И ЗначениеЗаполнено(ВремяОкончания) Тогда
		МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации("ВремяВыполненияОбработчиков",
			ВремяОкончания - ВремяНачала);
	КонецЕсли;
	
	ВремяНачала = СведенияОбОбновлении.ВремяНачалаОтложенногоОбновления;
	ВремяОкончания = СведенияОбОбновлении.ВремяОкончаниеОтложенногоОбновления;
	
	Если ЗначениеЗаполнено(ВремяНачала) И ЗначениеЗаполнено(ВремяОкончания) Тогда
		МодульЦентрМониторинга.ЗаписатьСтатистикуОбъектаКонфигурации("ВремяВыполненияОтложенныхОбработчиков",
			ВремяОкончания - ВремяНачала);
	КонецЕсли;
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхПодчиненному.
Процедура ПриОтправкеДанныхПодчиненному(ЭлементДанных, ОтправкаЭлемента, СозданиеНачальногоОбраза, Получатель) Экспорт
	
	ПриОтправкеВерсийПодсистем(ЭлементДанных, ОтправкаЭлемента, СозданиеНачальногоОбраза);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхГлавному.
Процедура ПриОтправкеДанныхГлавному(ЭлементДанных, ОтправкаЭлемента, Получатель) Экспорт
	
	ПриОтправкеВерсийПодсистем(ЭлементДанных, ОтправкаЭлемента);
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииПараметровРаботыКлиентаПриЗапуске.
Процедура ПриДобавленииПараметровРаботыКлиентаПриЗапуске(Параметры) Экспорт
	
	Параметры.Вставить("НачальноеЗаполнениеДанных", РежимОбновленияДанных() = "НачальноеЗаполнение");
	Параметры.Вставить("ПоказатьОписаниеИзмененийСистемы", ПоказатьОписаниеИзмененийСистемы());
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат;
	КонецЕсли;
	
	Если Пользователи.ЭтоПолноправныйПользователь(, Истина)
		И Не ОбновлениеИнформационнойБазы.НеобходимоОбновлениеИнформационнойБазы()
		И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбновлениеКонфигурации") Тогда
		Значение = ОбщегоНазначения.ХранилищеСистемныхНастроекЗагрузить("ПроверкаПатчей", "НомерВерсии");
		Если Значение = Неопределено
			Или ОбщегоНазначенияКлиентСервер.СравнитьВерсии(Метаданные.Версия, Значение) > 0 Тогда
			МодульОбновлениеКонфигурации = ОбщегоНазначения.ОбщийМодуль("ОбновлениеКонфигурации");
			Результат = МодульОбновлениеКонфигурации.ИсправленияИзменены();
			ОбщегоНазначения.ХранилищеСистемныхНастроекСохранить("ПроверкаПатчей", "НомерВерсии", Метаданные.Версия);
			Если Результат.ЕстьИзменения Тогда
				Сведения = СведенияОбОбновленииИнформационнойБазы();
				Сведения.ВерсияУдалениеПатчей = Метаданные.Версия;
				ЗаписатьСведенияОбОбновленииИнформационнойБазы(Сведения);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	СтатусОбработчиков = СтатусНевыполненныхОбработчиков();
	Если СтатусОбработчиков = "" Тогда
		Возврат;
	КонецЕсли;
	Если СтатусОбработчиков = "СтатусОшибка"
		И Пользователи.ЭтоПолноправныйПользователь(, Истина) Тогда
		Параметры.Вставить("ПоказатьСообщениеОбОшибочныхОбработчиках");
	Иначе
		Параметры.Вставить("ПоказатьОповещениеОНевыполненныхОбработчиках");
	КонецЕсли;
	
КонецПроцедуры

// См. ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковОбновления.
Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ПроверкаЛегальностиПолученияОбновления") Тогда
		Обработчик = Обработчики.Добавить();
		Обработчик.НачальноеЗаполнение = Истина;
		Обработчик.Процедура = "ОбновлениеИнформационнойБазыСлужебный.ЗаписатьПодтверждениеЛегальностиПолученияОбновлений";
	КонецЕсли;
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		Обработчик = Обработчики.Добавить();
		Обработчик.Версия = "3.0.2.160";
		Обработчик.Процедура = "ОбновлениеИнформационнойБазыСлужебный.УстановитьКлючРегламентногоЗадания";
		Обработчик.РежимВыполнения = "Оперативно";
	КонецЕсли;
	
КонецПроцедуры

// См. ОчередьЗаданийПереопределяемый.ПриПолученииСпискаШаблонов.
Процедура ПриПолученииСпискаШаблонов(ШаблоныЗаданий) Экспорт
	
	ШаблоныЗаданий.Добавить(Метаданные.РегламентныеЗадания.ОтложенноеОбновлениеИБ.Имя);
	
КонецПроцедуры

// Параметры:
//   ТекущиеДела - см. ТекущиеДелаСервер.ТекущиеДела.
//
Процедура ПриЗаполненииСпискаТекущихДел(ТекущиеДела) Экспорт
	
	МодульТекущиеДелаСервер = ОбщегоНазначения.ОбщийМодуль("ТекущиеДелаСервер");
	Если Не Пользователи.ЭтоПолноправныйПользователь(, Истина)
		Или МодульТекущиеДелаСервер.ДелоОтключено("ОтложенноеОбновление") Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат;
	КонецЕсли;
	
	// Процедура вызывается только при наличии подсистемы "Текущие дела", поэтому здесь
	// не делается проверка существования подсистемы.
	Разделы = МодульТекущиеДелаСервер.РазделыДляОбъекта(Метаданные.Обработки.РезультатыОбновленияПрограммы.ПолноеИмя());
	
	СтатусОбработчиков           = СтатусНевыполненныхОбработчиков();
	ЕстьОбработчикиСОшибкой      = (СтатусОбработчиков = "СтатусОшибка");
	ЕстьНеВыполненныеОбработчики = (СтатусОбработчиков = "СтатусНеВыполнено");
	ЕстьПриостановленныеОбработчики = (СтатусОбработчиков = "СтатусПриостановлен");
	
	Для Каждого Раздел Из Разделы Цикл
		Идентификатор = "ОтложенноеОбновление" + СтрЗаменить(Раздел.ПолноеИмя(), ".", "");
		Дело = ТекущиеДела.Добавить();
		Дело.Идентификатор = Идентификатор;
		Дело.ЕстьДела      = (ЕстьОбработчикиСОшибкой Или ЕстьНеВыполненныеОбработчики Или ЕстьПриостановленныеОбработчики);
		Дело.Важное        = ЕстьОбработчикиСОшибкой;
		Дело.Представление = НСтр("ru = 'Обновление программы не завершено'");
		Дело.Форма         = "Обработка.РезультатыОбновленияПрограммы.Форма.РезультатыОбновленияПрограммы";
		Дело.Владелец      = Раздел;
	КонецЦикла;
	
КонецПроцедуры

// См. ВариантыОтчетовПереопределяемый.НастроитьВариантыОтчетов.
Процедура ПриНастройкеВариантовОтчетов(Настройки) Экспорт
	МодульВариантыОтчетов = ОбщегоНазначения.ОбщийМодуль("ВариантыОтчетов");
	МодульВариантыОтчетов.НастроитьОтчетВМодулеМенеджера(Настройки, Метаданные.Отчеты.ПрогрессОтложенногоОбновления);
КонецПроцедуры

// Перезапускает отложенные обработчики, выполняющиеся только в главном узле
// при получении первого сообщения обмена.
//
Процедура ПриПолученииПервогоСообщенияОбменаРИБПослеОбновления() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	ИнформационнаяБазаФайловая = ОбщегоНазначения.ИнформационнаяБазаФайловая();
	СведенияОбОбновлении       = СведенияОбОбновленииИнформационнойБазы();
	Если СведенияОбОбновлении.ОтложенноеОбновлениеЗавершеноУспешно = Неопределено
		И Не ИнформационнаяБазаФайловая Тогда
		ОтключитьОтложенноеОбновление();
		ПараметрыОтбора = Новый Структура;
		ПараметрыОтбора.Вставить("ИмяМетода", "ОбновлениеИнформационнойБазыСлужебный.ВыполнитьОтложенноеОбновление");
		ПараметрыОтбора.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);
		МассивФоновыхЗаданий = ФоновыеЗадания.ПолучитьФоновыеЗадания(ПараметрыОтбора);
		Если МассивФоновыхЗаданий.Количество() = 1 Тогда
			ФоновоеЗадание = МассивФоновыхЗаданий[0];
			ФоновоеЗадание.Отменить();
		КонецЕсли;
	КонецЕсли;
	
	ИтерацииОбновления = ИтерацииОбновления();
	Обработки.ОписаниеОбработчиковОбновления.ЗаполнитьНомерОчереди(ИтерацииОбновления);
	ОбновитьСписокВыполняемыхОбработчиковОбновления(ИтерацииОбновления, Истина);
	ПеререгистрироватьДанныеДляОтложенногоОбновления();
	Если ИнформационнаяБазаФайловая Тогда
		ВыполнитьОтложенноеОбновлениеСейчас();
	Иначе
		ЗапланироватьОтложенноеОбновление();
	КонецЕсли;
	
КонецПроцедуры

// Вызывается при выполнении скрипта обновления из процедуры ОбновлениеКонфигурации.ЗавершитьОбновление.
Процедура ПослеЗавершенияОбновления() Экспорт
	
	ЗаписатьПодтверждениеЛегальностиПолученияОбновлений();
	
КонецПроцедуры

// См. ВыгрузкаЗагрузкаДанныхПереопределяемый.ПослеЗагрузкиДанных.
Процедура ПослеЗагрузкиДанных(Контейнер) Экспорт
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	
	Если СведенияОбОбновлении.ОтложенноеОбновлениеЗавершеноУспешно <> Истина Тогда
		ЗапланироватьОтложенноеОбновление();
	КонецЕсли;
КонецПроцедуры

// См. ПодключаемыеКомандыПереопределяемый.ПриОпределенииКомандПодключенныхКОбъекту
Процедура ПриОпределенииКомандПодключенныхКОбъекту(НастройкиФормы, Источники, ПодключенныеОтчетыИОбработки, Команды) Экспорт
	
	Команда = Команды.Добавить();
	Команда.Вид = "ОбновлениеВерсииИБ";
	Команда.Представление = НСтр("ru = 'Разблокировать объект для редактирования'");
	Команда.РежимЗаписи = "НеЗаписывать";
	Команда.Назначение = "ДляОбъекта";
	Команда.ТолькоВоВсехДействиях = Истина;
	Команда.Обработчик = "ОбновлениеИнформационнойБазыКлиент.РазблокироватьОбъектДляРедактирования";
	
	МодульПодключаемыеКоманды = ОбщегоНазначения.ОбщийМодуль("ПодключаемыеКоманды");
	МодульПодключаемыеКоманды.ДобавитьУсловиеВидимостиКоманды(Команда, "ОбновлениеВерсииИБ_ОбъектЗаблокирован");
	
КонецПроцедуры

// См. ПодключаемыеКомандыПереопределяемый.ПриОпределенииВидовПодключаемыхКоманд
Процедура ПриОпределенииВидовПодключаемыхКоманд(ВидыПодключаемыхКоманд) Экспорт
	
	Вид = ВидыПодключаемыхКоманд.Добавить();
	Вид.Имя = "ОбновлениеВерсииИБ";
	Вид.Заголовок   = НСтр("ru = 'Разблокировка объекта'");
	Вид.Отображение = ОтображениеКнопки.КартинкаИТекст;
	
КонецПроцедуры

// См. ОчередьЗаданийПереопределяемый.ПриОпределенииПсевдонимовОбработчиков.
Процедура ПриОпределенииПсевдонимовОбработчиков(СоответствиеИменПсевдонимам) Экспорт
	
	СоответствиеИменПсевдонимам.Вставить("ОбновлениеИнформационнойБазы.ПерезапуститьОтложенноеОбновление");
	СоответствиеИменПсевдонимам.Вставить(Метаданные.РегламентныеЗадания.ОчисткаУстаревшихДанных.ИмяМетода);
	
КонецПроцедуры

// См. КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПроверок.
Процедура ПриОпределенииПроверок(ГруппыПроверок, Проверки) Экспорт
	
	Проверка = Проверки.Добавить();
	Проверка.ИдентификаторГруппы          = "СистемныеПроверки";
	Проверка.Наименование                 = НСтр("ru = 'Проблема с данными при обновлении на новую версию'");
	Проверка.Причины                      = НСтр("ru = 'Некорректная синхронизация данных с другими программами или импорт данных,
		|ошибки в сторонних инструментах (например, внешних обработках или расширениях) или сбои в работе оборудования.'");
	Проверка.Рекомендация                 = НСтр("ru = 'Если не заполнены обязательные реквизиты, то заполнить их вручную.
		|Восстановить отсутствующие данные из резервной копии.'");
	Проверка.Идентификатор                = "ОбновлениеИнформационнойБазыПроблемаСДанными";
	Проверка.ОбработчикПроверки           = "ОбновлениеИнформационнойБазыСлужебный.ОбработчикПроверкиВеденияУчета";
	Проверка.ЗапрещеноИзменениеВажности   = Ложь;
	Проверка.КонтекстПроверокВеденияУчета = "ОбновлениеВерсииИБ";
	Проверка.Отключена                    = Истина;
	
КонецПроцедуры

Функция НастройкиПодсистемы() Экспорт
	
	ПараметрыСообщенияОНевыполненныхОтложенныхОбработчиках = Новый Структура;
	ПараметрыСообщенияОНевыполненныхОтложенныхОбработчиках.Вставить("ТекстСообщения", "");
	ПараметрыСообщенияОНевыполненныхОтложенныхОбработчиках.Вставить("КартинкаСообщения", Неопределено);
	ПараметрыСообщенияОНевыполненныхОтложенныхОбработчиках.Вставить("ЗапрещатьПродолжение", Ложь);
	
	
	Настройки = Новый Структура;
	Настройки.Вставить("ПоясненияДляРезультатовОбновления", "");
	Настройки.Вставить("РасположениеОписанияИзмененийПрограммы", "");
	Настройки.Вставить("ПараметрыСообщенияОНевыполненныхОтложенныхОбработчиках", ПараметрыСообщенияОНевыполненныхОтложенныхОбработчиках);
	Настройки.Вставить("МногопоточноеОбновление", Ложь);
	Настройки.Вставить("КоличествоПотоковОбновленияИнформационнойБазыПоУмолчанию", 1);
	
	Настройки.Вставить("ОбъектыСНачальнымЗаполнением", Новый Массив);
	ИнтеграцияПодсистемБСП.ПриОпределенииОбъектовСНачальнымЗаполнением(Настройки.ОбъектыСНачальнымЗаполнением);
	
	ОбновлениеИнформационнойБазыПереопределяемый.ПриОпределенииНастроек(Настройки);
	
	Возврат Настройки;
	
КонецФункции

// Начальное заполнение предопределенных данных

// Возвращаемое значение:
//  Массив из ОбъектМетаданных
//
Функция ОбъектыСНачальнымЗаполнением() Экспорт
	
	НастройкиПодсистемы = НастройкиПодсистемы();
	ОбъектыСНачальнымЗаполнением = НастройкиПодсистемы.ОбъектыСНачальнымЗаполнением;
	Возврат ОбъектыСНачальнымЗаполнением;
	
КонецФункции

Функция НастройкиПредопределенныхЭлементов(МенеджерОбъекта, ПредопределенныеДанные) Экспорт
	
	ПользовательскиеНастройкиЗаполненияЭлементов = ПользовательскиеНастройкиЗаполненияЭлементов();
	ПользовательскиеНастройкиЗаполненияЭлементов.ДополнительныеПараметры.Вставить("ПредопределенныеДанные", ПредопределенныеДанные);
	
	ПолноеИмяОбъекта = МенеджерОбъекта.ПустаяСсылка().Метаданные().ПолноеИмя();
	
	МенеджерОбъекта.ПриНастройкеНачальногоЗаполненияЭлементов(ПользовательскиеНастройкиЗаполненияЭлементов);
	ОбновлениеИнформационнойБазыПереопределяемый.ПриНастройкеНачальногоЗаполненияЭлементов(ПолноеИмяОбъекта,
		ПользовательскиеНастройкиЗаполненияЭлементов);
		
	НастройкиЗаполненияЭлементов = НастройкиЗаполненияЭлементов();
	ЗаполнитьЗначенияСвойств(НастройкиЗаполненияЭлементов.ПереопределяемыеНастройки, ПользовательскиеНастройкиЗаполненияЭлементов);
	
	НастройкиЗаполненияЭлементов.ЕстьКолонкаИмяПредопределенныхДанных = 
		 ПредопределенныеДанные.Колонки.Найти("ИмяПредопределенныхДанных") <> Неопределено;
		 
	НастройкиЗаполненияЭлементов.ПереопределяемыеНастройки.ИмяКлючевогоРеквизита =
		?(ЗначениеЗаполнено(НастройкиЗаполненияЭлементов.ПереопределяемыеНастройки.ИмяКлючевогоРеквизита),
				НастройкиЗаполненияЭлементов.ПереопределяемыеНастройки.ИмяКлючевогоРеквизита, "ИмяПредопределенныхДанных");
	
	Возврат НастройкиЗаполненияЭлементов;
	
КонецФункции

// Описание настроек заполнения элементов
// 
// Возвращаемое значение:
//  Структура:
//    * ПереопределяемыеНастройки - см. ПользовательскиеНастройкиЗаполненияЭлементов
//    * ЕстьКолонкаИмяПредопределенныхДанных - Булево
//
Функция НастройкиЗаполненияЭлементов() Экспорт
	
	НастройкиЗаполненияЭлементов = Новый Структура;
	НастройкиЗаполненияЭлементов.Вставить("ПереопределяемыеНастройки", ПользовательскиеНастройкиЗаполненияЭлементов());
	НастройкиЗаполненияЭлементов.Вставить("ЕстьКолонкаИмяПредопределенныхДанных", Ложь);
	
	Возврат НастройкиЗаполненияЭлементов;
	
КонецФункции

Функция ПредопределенныеДанныеОбъекта(Знач МетаданныеОбъекта, МенеджерОбъекта, ЛокализуемыеРеквизитыОбъекта) Экспорт
	
	Языки = СтандартныеПодсистемыСервер.ЯзыкиКонфигурации();
	
	// Таблица с предопределенными данными
	ПредопределенныеДанные = Новый ТаблицаЗначений;
	ТабличныеЧасти         = Новый Структура;
	
	Для Каждого Реквизит Из МетаданныеОбъекта.СтандартныеРеквизиты Цикл
		ДобавитьКолонкуТаблицыПредопределенныхДанных(ПредопределенныеДанные, Реквизит, ЛокализуемыеРеквизитыОбъекта, Языки);
	КонецЦикла;
	
	Для Каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
		ДобавитьКолонкуТаблицыПредопределенныхДанных(ПредопределенныеДанные, Реквизит, ЛокализуемыеРеквизитыОбъекта, Языки);
	КонецЦикла;
	
	Для Каждого ТабличнаяЧасть Из МетаданныеОбъекта.ТабличныеЧасти Цикл
		
		ПредопределенныеДанные.Колонки.Добавить(ТабличнаяЧасть.Имя, Новый ОписаниеТипов("ТаблицаЗначений"));
		Таблица = Новый ТаблицаЗначений;
		Для Каждого Реквизит Из ТабличнаяЧасть.СтандартныеРеквизиты Цикл
			ДобавитьКолонкуТаблицыПредопределенныхДанных(Таблица, Реквизит, ЛокализуемыеРеквизитыОбъекта, Языки);
		КонецЦикла;
		
		Для Каждого Реквизит Из ТабличнаяЧасть.Реквизиты Цикл
			ДобавитьКолонкуТаблицыПредопределенныхДанных(Таблица, Реквизит, ЛокализуемыеРеквизитыОбъекта, Языки);
		КонецЦикла;
		ТабличныеЧасти.Вставить(ТабличнаяЧасть.Имя, Таблица);
		
	КонецЦикла;
	
	Если ОбщегоНазначения.ЭтоПланСчетов(МетаданныеОбъекта)
		 Или ОбщегоНазначения.ЭтоПланВидовРасчета(МетаданныеОбъекта) Тогда
			Для Каждого ТабличнаяЧасть Из МетаданныеОбъекта.СтандартныеТабличныеЧасти Цикл
				
				ПредопределенныеДанные.Колонки.Добавить(ТабличнаяЧасть.Имя, Новый ОписаниеТипов("ТаблицаЗначений"));
				Таблица = Новый ТаблицаЗначений;
				Для Каждого Реквизит Из ТабличнаяЧасть.СтандартныеРеквизиты Цикл
					ДобавитьКолонкуТаблицыПредопределенныхДанных(Таблица, Реквизит, ЛокализуемыеРеквизитыОбъекта, Языки);
				КонецЦикла;
				
				ТабличныеЧасти.Вставить(ТабличнаяЧасть.Имя, Таблица);
				
			КонецЦикла;
	КонецЕсли;
	
	Если ПредопределенныеДанные.Колонки.Найти("ИмяПредопределенныхДанных") <> Неопределено Тогда
		ПредопределенныеДанные.Индексы.Добавить("ИмяПредопределенныхДанных");
	КонецЕсли;
	
	МенеджерОбъекта.ПриНачальномЗаполненииЭлементов(Языки, ПредопределенныеДанные, ТабличныеЧасти);
	ОбновлениеИнформационнойБазыПереопределяемый.ПриНачальномЗаполненииЭлементов(МетаданныеОбъекта.ПолноеИмя(), Языки, ПредопределенныеДанные, ТабличныеЧасти);
	
	Если ПредопределенныеДанные.Колонки.Найти("Ссылка") <> Неопределено Тогда
		Для Каждого СтрокаПредопределенныхДанных Из ПредопределенныеДанные Цикл
			
			Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
				
				Если ТипЗнч(СтрокаПредопределенныхДанных.Ссылка) = Тип("УникальныйИдентификатор") Тогда
					СтрокаПредопределенныхДанных.Ссылка = МенеджерОбъекта.ПолучитьСсылку(СтрокаПредопределенныхДанных.Ссылка);
				ИначеЕсли ТипЗнч(СтрокаПредопределенныхДанных.Ссылка) = Тип("Строка") Тогда
					СтрокаПредопределенныхДанных.Ссылка = МенеджерОбъекта.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаПредопределенныхДанных.Ссылка));
				КонецЕсли; 
				
			КонецЕсли;
			
		КонецЦикла;
	КонецЕсли;
	
	Возврат ПредопределенныеДанные;
	
КонецФункции

// Параметры:
//  ОбновитьМультиязычныеСтроки - Булево - если Истина, то будут перезаполнены, только мультиязычные реквизиты.
//  ДополнительныеПараметры - см. МультиязычностьСервер.ОписаниеСтарыхИНовыхНастроекЯзыков
// 
Процедура НачальноеЗаполнениеПредопределенныхДанных(ОбновитьМультиязычныеСтроки = Ложь, ДополнительныеПараметры = Неопределено) Экспорт
	
	ОбъектыСНачальнымЗаполнением = ОбъектыСНачальнымЗаполнением();
	ПараметрыЗаполнения = Новый Структура;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
		МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
		МодульМультиязычностьСервер.ПриПолученииПараметровЗаполненияПредопределенныхДанных(ПараметрыЗаполнения);
	КонецЕсли;
	
	ПроверятьНеобходимостьВыполнения = ДополнительныеПараметры <> Неопределено
		И ТипЗнч(ДополнительныеПараметры) = Тип("Структура")
		И ДополнительныеПараметры.Свойство("НовыеОбъекты");
	
	Для Каждого МетаданныеОбъекта Из ОбъектыСНачальнымЗаполнением Цикл
		
		Если ПроверятьНеобходимостьВыполнения
			И ДополнительныеПараметры.НовыеОбъекты.Найти(МетаданныеОбъекта.ПолноеИмя()) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		ПараметрыЗаполнения = НаборПараметровДляЗаполненияОбъекта(МетаданныеОбъекта);

		МенеджерОбъекта = ПараметрыЗаполнения.МенеджерОбъекта;
		ПредопределенныеДанные = ПараметрыЗаполнения.ПредопределенныеДанные;
		НастройкиЗаполнения = ПараметрыЗаполнения.НастройкиПредопределенныхЭлементов;
		
		ИмяКлючевогоРеквизита = НастройкиЗаполнения.ПереопределяемыеНастройки.ИмяКлючевогоРеквизита;
		 
		СуществующиеЭлементы = СуществующиеПоставляемыеЭлементы(ПредопределенныеДанные, НастройкиЗаполнения, МенеджерОбъекта, МетаданныеОбъекта); // @skip-check query-in-loop - механизм обновления
		
		ТабличныеЧасти = Новый Массив;
		ОбъектМетаданныхТабличныеЧасти = МетаданныеОбъекта.ТабличныеЧасти; // КоллекцияОбъектовМетаданных из ОбъектМетаданныхТабличнаяЧасть
		Для Каждого ТабличнаяЧасть Из ОбъектМетаданныхТабличныеЧасти Цикл
			Если ПредопределенныеДанные.Колонки.Найти(ТабличнаяЧасть.Имя) <> Неопределено Тогда
				ТабличныеЧасти.Добавить(ТабличнаяЧасть.Имя);
			КонецЕсли;
		КонецЦикла;
		
		РеквизитыИсключения = Новый Соответствие;
		Если СтрНачинаетсяС(МетаданныеОбъекта.ПолноеИмя(), "ПланВидовХарактеристик") Тогда
			РеквизитыИсключения.Вставить("ТипЗначения", Истина);
		КонецЕсли;
		
		ПоддерживаетсяИерархия =  ПредопределенныеДанные.Колонки.Найти("ЭтоГруппа") <> Неопределено;
		РеквизитыИсключения.Вставить("Родитель", Истина); // Поле заполняется отдельно
		
		РеквизитыИсключенияСЭлементами = Новый Соответствие;
		Если ПоддерживаетсяИерархия Тогда
			ОбщегоНазначенияКлиентСервер.ДополнитьСоответствие(РеквизитыИсключенияСЭлементами, РеквизитыИсключения);
			ДляЭлемента = Метаданные.СвойстваОбъектов.ИспользованиеРеквизита.ДляЭлемента;
			Для Каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
				Если Реквизит.Использование = ДляЭлемента Тогда
					РеквизитыИсключенияСЭлементами.Вставить(Реквизит.Имя, Истина);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		ЕстьКолонкаСсылка = ПредопределенныеДанные.Колонки.Найти("Ссылка") <> Неопределено;
		
		НачатьТранзакцию();
		Попытка
			
			Для каждого СтрокаТаблицы Из ПредопределенныеДанные Цикл
				
				СсылкаНаОбъект = Неопределено;
				
				Если НастройкиЗаполнения.ЕстьКолонкаИмяПредопределенныхДанных Тогда
					СсылкаНаОбъект = СуществующиеЭлементы[СтрокаТаблицы.ИмяПредопределенныхДанных];
				КонецЕсли;
				
				Если СсылкаНаОбъект = Неопределено И ЗначениеЗаполнено(СтрокаТаблицы[ИмяКлючевогоРеквизита]) Тогда
					СсылкаНаОбъект = СуществующиеЭлементы[СтрокаТаблицы[ИмяКлючевогоРеквизита]];
				КонецЕсли;
					
				Если СсылкаНаОбъект <> Неопределено Тогда
					
					БлокировкаДанных = Новый БлокировкаДанных;
					ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(МетаданныеОбъекта.ПолноеИмя());
					ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", СсылкаНаОбъект);
					БлокировкаДанных.Заблокировать();
					
					ЗаполняемыйЭлемент = СсылкаНаОбъект.ПолучитьОбъект();
					
				Иначе
					
					Если ПоддерживаетсяИерархия И СтрокаТаблицы.ЭтоГруппа = Истина Тогда
						ЗаполняемыйЭлемент = МенеджерОбъекта.СоздатьГруппу();
					Иначе
						ЗаполняемыйЭлемент = МенеджерОбъекта.СоздатьЭлемент();
					КонецЕсли;
					
					Если ЕстьКолонкаСсылка И ЗначениеЗаполнено(СтрокаТаблицы.Ссылка) Тогда
						
						ЗаполняемыйЭлемент.УстановитьСсылкуНового(СтрокаТаблицы.Ссылка);
						
						Если ЗначениеЗаполнено(СтрокаТаблицы[ИмяКлючевогоРеквизита]) Тогда
							СуществующиеЭлементы.Вставить(СтрокаТаблицы[ИмяКлючевогоРеквизита], СтрокаТаблицы.Ссылка);
						КонецЕсли;
					
					Иначе
						
						НоваяСсылка = МенеджерОбъекта.ПолучитьСсылку();
						ЗаполняемыйЭлемент.УстановитьСсылкуНового(НоваяСсылка);
					
						Если ЗначениеЗаполнено(СтрокаТаблицы[ИмяКлючевогоРеквизита]) Тогда
							СуществующиеЭлементы.Вставить(СтрокаТаблицы[ИмяКлючевогоРеквизита], НоваяСсылка);
						КонецЕсли;
						
					КонецЕсли;
				
				КонецЕсли;
				
				Если Не ОбновитьМультиязычныеСтроки Тогда
					
					Если ПоддерживаетсяИерархия И ЗначениеЗаполнено(СтрокаТаблицы.Родитель) Тогда
						
						Если ИмяКлючевогоРеквизита = "Ссылка" Тогда
							Если ТипЗнч(СтрокаТаблицы.Родитель) = Тип("Строка") Тогда
								ЗаполняемыйЭлемент.Родитель = МенеджерОбъекта.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаТаблицы.Родитель));
							ИначеЕсли ТипЗнч(СтрокаТаблицы.Родитель) = Тип("УникальныйИдентификатор") Тогда
								ЗаполняемыйЭлемент.Родитель = МенеджерОбъекта.ПолучитьСсылку(СтрокаТаблицы.Родитель);
							Иначе
								ЗаполняемыйЭлемент.Родитель = СтрокаТаблицы.Родитель;
							КонецЕсли;
						ИначеЕсли ТипЗнч(СтрокаТаблицы.Родитель) = Тип("Строка") Тогда
							ЗаполняемыйЭлемент.Родитель = СуществующиеЭлементы[СтрокаТаблицы.Родитель];
						Иначе
							ЗаполняемыйЭлемент.Родитель = СтрокаТаблицы.Родитель;
						КонецЕсли;
					
					КонецЕсли;
					
					ПоляИсключения =?(ПоддерживаетсяИерархия И ЗаполняемыйЭлемент.ЭтоГруппа,
						РеквизитыИсключенияСЭлементами, РеквизитыИсключения);
					
					ДанныеЗаполнения = ОпределитьДанныеЗаполнения(ПредопределенныеДанные, СтрокаТаблицы, ПоляИсключения);
					
					ЗаполнитьЗначенияСвойств(ЗаполняемыйЭлемент, ДанныеЗаполнения);
					
					Если Не (ПоддерживаетсяИерархия И ЗаполняемыйЭлемент.ЭтоГруппа) Тогда
						Для Каждого ИмяТабличнойЧасти Из ТабличныеЧасти Цикл
							Если СтрокаТаблицы[ИмяТабличнойЧасти].Количество() > 0 Тогда
								ЗаполняемыйЭлемент[ИмяТабличнойЧасти].Загрузить(СтрокаТаблицы[ИмяТабличнойЧасти]);
							КонецЕсли;
						КонецЦикла;
					КонецЕсли;
					
				КонецЕсли;
				
				Если ПараметрыЗаполнения.ЛокализуемыеРеквизитыОбъекта.Количество() > 0 И ПараметрыЗаполнения.ЛокализуемыеРеквизитыОбъекта["Наименование"] <> Неопределено Тогда
					Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
						МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
						МодульМультиязычностьСервер.НачальноеЗаполнениеПредопределенныхДанныхЛокализуемыеРеквизиты(
							ЗаполняемыйЭлемент, ПоддерживаетсяИерархия, СтрокаТаблицы, ПараметрыЗаполнения);
					ИначеЕсли ПараметрыЗаполнения.ОбъектСодержитТЧПредставления Тогда
						НачальноеЗаполнениеТЧПредставления(ЗаполняемыйЭлемент, СтрокаТаблицы, ПараметрыЗаполнения);
					КонецЕсли;
				ИначеЕсли ОбновитьМультиязычныеСтроки
						  И ПредопределенныеДанные.Колонки.Найти("Наименование") <> Неопределено
						  И ЗначениеЗаполнено(СтрокаТаблицы["Наименование"]) Тогда
							ЗаполняемыйЭлемент["Наименование"] = СтрокаТаблицы["Наименование"];
				КонецЕсли;
				
				Если Не ОбновитьМультиязычныеСтроки И НастройкиЗаполнения.ПереопределяемыеНастройки.ПриНачальномЗаполненииЭлемента Тогда
					
					МенеджерОбъекта.ПриНачальномЗаполненииЭлемента(ЗаполняемыйЭлемент, СтрокаТаблицы, НастройкиЗаполнения.ПереопределяемыеНастройки.ДополнительныеПараметры);
					ОбновлениеИнформационнойБазыПереопределяемый.ПриНачальномЗаполненииЭлемента(МетаданныеОбъекта.ПолноеИмя(),
						ЗаполняемыйЭлемент, СтрокаТаблицы, НастройкиЗаполнения.ПереопределяемыеНастройки.ДополнительныеПараметры);
					
				КонецЕсли;
				
				ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ЗаполняемыйЭлемент);
				
				СтрокаТаблицы.Ссылка = ЗаполняемыйЭлемент.Ссылка;
				
			КонецЦикла;
			
			ЗафиксироватьТранзакцию();
			
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
	КонецЦикла;
	
	Если ОбновитьМультиязычныеСтроки Тогда
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
			МодульУправлениеДоступомСлужебный = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступомСлужебный");
			МодульУправлениеДоступомСлужебный.ПриИзмененииЯзыкаИнформационнойБазы(ДополнительныеПараметры);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

Процедура НачальноеЗаполнениеТЧПредставления(ЗаполняемыйЭлемент, СтрокаТаблицы, ПараметрыЗаполнения) Экспорт
	
	Языки = СтандартныеПодсистемыСервер.ЯзыкиКонфигурации();
	ЛокализуемыеРеквизитыОбъекта = ПараметрыЗаполнения.ЛокализуемыеРеквизитыОбъекта;
	
	Для каждого КодЯзыка Из Языки Цикл
		Если СтрСравнить(КодЯзыка, ОбщегоНазначения.КодОсновногоЯзыка()) = 0 Тогда
			Продолжить;
		КонецЕсли;
		ПредставленияОбъекта = ЗаполняемыйЭлемент.Представления; // ТаблицаЗначений
		НовоеПредставление = ПредставленияОбъекта.Добавить();
		НовоеПредставление.КодЯзыка = КодЯзыка;
		Для каждого ИмяЛокализуемогоРеквизита Из ЛокализуемыеРеквизитыОбъекта Цикл
			Значение = СтрокаТаблицы[ИмяЛокализуемогоРеквизита.Ключ + "_" + КодЯзыка];
			НовоеПредставление[ИмяЛокализуемогоРеквизита.Ключ] = ?(ЗначениеЗаполнено(Значение), Значение, СтрокаТаблицы[ИмяЛокализуемогоРеквизита.Ключ]);
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

Функция НаборПараметровДляЗаполненияОбъекта(МетаданныеОбъекта) Экспорт
	
	Результат = Новый Структура;
	
	ОбъектыСНачальнымЗаполнением = ОбъектыСНачальнымЗаполнением();
	ЕстьПредопределенныеДанные =  ОбъектыСНачальнымЗаполнением.Найти(МетаданныеОбъекта) <> Неопределено;
	
	ЛокализуемыеРеквизитыОбъекта = Новый Соответствие;
	МультиязычныеСтрокиВРеквизитах = Ложь;
	ОбъектСодержитТЧПредставления  = Ложь;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
		МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
		ЛокализуемыеРеквизитыОбъекта = МодульМультиязычностьСервер.МультиязычныеРеквизитыОбъекта(МетаданныеОбъекта);
		МультиязычныеСтрокиВРеквизитах = МодульМультиязычностьСервер.МультиязычныеСтрокиВРеквизитах(МетаданныеОбъекта);
		ОбъектСодержитТЧПредставления = МодульМультиязычностьСервер.ОбъектСодержитТЧПредставления(МетаданныеОбъекта.ПолноеИмя());
	КонецЕсли;
	
	МенеджерОбъекта = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(МетаданныеОбъекта.ПолноеИмя());
	
	Если ЕстьПредопределенныеДанные Тогда
		ПредопределенныеДанные = ПредопределенныеДанныеОбъекта(МетаданныеОбъекта, МенеджерОбъекта, ЛокализуемыеРеквизитыОбъекта);
		НастройкиПредопределенныхЭлементов = НастройкиПредопределенныхЭлементов(МенеджерОбъекта, ПредопределенныеДанные);
	Иначе
		ПредопределенныеДанные = Новый ТаблицаЗначений();
		НастройкиПредопределенныхЭлементов = Новый Структура;
	КонецЕсли;
	
	ТабличныеЧасти = Новый Массив;
	ОбъектМетаданныхТабличныеЧасти = МетаданныеОбъекта.ТабличныеЧасти; // КоллекцияОбъектовМетаданных из ОбъектМетаданныхТабличнаяЧасть
	Для Каждого ТабличнаяЧасть Из ОбъектМетаданныхТабличныеЧасти Цикл
		Если ПредопределенныеДанные.Колонки.Найти(ТабличнаяЧасть.Имя) <> Неопределено Тогда
			ТабличныеЧасти.Добавить(ТабличнаяЧасть.Имя);
		КонецЕсли;
	КонецЦикла;
	
	РеквизитыИсключения    = ?(СтрНачинаетсяС(МетаданныеОбъекта.ПолноеИмя(), "ПланВидовХарактеристик"), "ТипЗначения", "");
	ПоддерживаетсяИерархия =  ПредопределенныеДанные.Колонки.Найти("ЭтоГруппа") <> Неопределено;
	
	ТЧПредставленияИспользуетсяДляГрупп = Ложь; 
	Если ОбъектСодержитТЧПредставления И ПоддерживаетсяИерархия Тогда
		 
		Если МетаданныеОбъекта.ТабличныеЧасти.Представления.Использование = Метаданные.СвойстваОбъектов.ИспользованиеРеквизита.ДляГруппыИЭлемента
			Или МетаданныеОбъекта.ТабличныеЧасти.Представления.Использование = Метаданные.СвойстваОбъектов.ИспользованиеРеквизита.ДляГруппы Тогда
			ТЧПредставленияИспользуетсяДляГрупп = Истина;
		КонецЕсли;
		
	КонецЕсли;
	
	РеквизитыСЭлементами = Новый Массив;
	Если ПоддерживаетсяИерархия Тогда
		ДляЭлемента = Метаданные.СвойстваОбъектов.ИспользованиеРеквизита.ДляЭлемента;
		Для Каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
			Если Реквизит.Использование = ДляЭлемента Тогда
				РеквизитыСЭлементами.Добавить(Реквизит.Имя);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Результат.Вставить("НастройкиПредопределенныхЭлементов", НастройкиПредопределенныхЭлементов);
	Результат.Вставить("ПредопределенныеДанные", ПредопределенныеДанные);
	Результат.Вставить("РеквизитыИсключения", РеквизитыИсключения);
	Результат.Вставить("ПоддерживаетсяИерархия", ПоддерживаетсяИерархия);
	Результат.Вставить("ТабличныеЧасти", ТабличныеЧасти);
	Результат.Вставить("ЛокализуемыеРеквизитыОбъекта", ЛокализуемыеРеквизитыОбъекта);
	Результат.Вставить("МультиязычныеСтрокиВРеквизитах", МультиязычныеСтрокиВРеквизитах);
	Результат.Вставить("ОбъектСодержитТЧПредставления", ОбъектСодержитТЧПредставления);
	Результат.Вставить("ТЧПредставленияИспользуетсяДляГрупп", ТЧПредставленияИспользуетсяДляГрупп);
	Результат.Вставить("МенеджерОбъекта", МенеджерОбъекта);
	Результат.Вставить("РеквизитыСЭлементами", РеквизитыСЭлементами);
	
	Возврат Результат;
	
КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Функция ОпределитьДанныеЗаполнения(Знач ПредопределенныеДанные, Знач СтрокаТаблицы, Знач ПоляИсключения)
	
	ДанныеЗаполнения = Новый Структура;
	Для Каждого Колонка Из ПредопределенныеДанные.Колонки Цикл
		
		ИмяПоля = Колонка.Имя;
		Если ПоляИсключения[ИмяПоля] = Истина Тогда
			Продолжить;
		КонецЕсли;
		
		Значение = СтрокаТаблицы[ИмяПоля];
		Если Колонка.ТипЗначения.Типы().Количество() > 1 Тогда
			Заполнено = Значение <> Неопределено;
		ИначеЕсли ТипЗнч(Значение) = Тип("Булево") Тогда
			Заполнено = Значение;
		Иначе
			Заполнено = ЗначениеЗаполнено(Значение);
		КонецЕсли;
		
		Если Заполнено Тогда
			ДанныеЗаполнения.Вставить(ИмяПоля, Значение);
		КонецЕсли;
	КонецЦикла;
	
	Возврат ДанныеЗаполнения;
	
КонецФункции

Функция ДействияПередОбновлениемИнформационнойБазы(ПараметрыОбновления)
	
	Если Не ПараметрыОбновления.ПриЗапускеКлиентскогоПриложения Тогда
		Если Не ОбщегоНазначения.РазделениеВключено()
			И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбновлениеКонфигурации") Тогда
			МодульОбновлениеКонфигурации = ОбщегоНазначения.ОбщийМодуль("ОбновлениеКонфигурации");
			МодульОбновлениеКонфигурации.ПроверитьНаличиеУстаревшихИсправлений();
		КонецЕсли;
		
		Попытка
			РегистрыСведений.ПараметрыРаботыПрограммы.ЗагрузитьОбновитьПараметрыРаботыПрограммы();
		Исключение
			ЗаписатьОшибку(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			ВызватьИсключение;
		КонецПопытки;
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("Возврат", "");
	
	// Определяем факт смены имени конфигурации.
	
	РежимОбновленияДанных = РежимОбновленияДанных();
	ВерсияМетаданных = Метаданные.Версия;
	Если ПустаяСтрока(ВерсияМетаданных) Тогда
		ВерсияМетаданных = "0.0.0.0";
	КонецЕсли;
	ВерсияДанных = ВерсияИБ(Метаданные.Имя);
	
	// Перед обновлением информационной базы.
	//
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ПередОбновлениемИнформационнойБазы();
		
		// Установка привилегированного режима для возможности обновления ИБ в модели сервиса,
		// в случае когда администратор области данных выполняет вход в область до завершения обновления области.
		Если ОбщегоНазначения.РазделениеВключено() И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
			УстановитьПривилегированныйРежим(Истина);
		КонецЕсли;
		
	КонецЕсли;
	
	НастроитьЗаданиеОчисткаУстаревшихДанных(Ложь);
	
	// Загрузка и выгрузка сообщения обмена после перезапуска в связи с получением изменений конфигурации.
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
		МодульОбменДаннымиСервер.ПередОбновлениемИнформационнойБазы(ПараметрыОбновления.ПриЗапускеКлиентскогоПриложения, ПараметрыОбновления.Перезапустить);
	КонецЕсли;
	
	Если НЕ ОбновлениеИнформационнойБазы.НеобходимоОбновлениеИнформационнойБазы() Тогда
		Результат.Возврат = "НеТребуется";
		Возврат Результат;
	КонецЕсли;
	
	Если ПараметрыОбновления.ВФоне Тогда
		ДлительныеОперации.СообщитьПрогресс(1);
	КонецЕсли;
	
	ОписанияПодсистем  = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем();
	Для каждого ИмяПодсистемы Из ОписанияПодсистем.Порядок Цикл
		ОписаниеПодсистемы = ОписанияПодсистем.ПоИменам.Получить(ИмяПодсистемы);
		Если НЕ ЗначениеЗаполнено(ОписаниеПодсистемы.ОсновнойСерверныйМодуль) Тогда
			Продолжить;
		КонецЕсли;
		Модуль = ОбщегоНазначения.ОбщийМодуль(ОписаниеПодсистемы.ОсновнойСерверныйМодуль);
		Модуль.ПередОбновлениемИнформационнойБазы();
	КонецЦикла;
	ОбновлениеИнформационнойБазыПереопределяемый.ПередОбновлениемИнформационнойБазы();
	
	// Проверка наличия прав для обновления информационной базы.
	Если НЕ ЕстьПраваНаОбновлениеИнформационнойБазы() Тогда
		Сообщение = НСтр("ru = 'Недостаточно прав для обновления версии программы.'");
		ЗаписатьОшибку(Сообщение);
		ВызватьИсключение Сообщение;
	КонецЕсли;
	
	Если РежимОбновленияДанных = "ПереходСДругойПрограммы" Тогда
		Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Изменилось имя конфигурации на ""%1"".
			|Будет выполнен переход с другой программы.'"),
			Метаданные.Имя);
	ИначеЕсли РежимОбновленияДанных = "ОбновлениеВерсии" Тогда
		Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Изменился номер версии конфигурации: с ""%1"" на ""%2"".
			|Будет выполнено обновление информационной базы.'"),
			ВерсияДанных, ВерсияМетаданных);
	Иначе
		Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Выполняется начальное заполнение данных до версии ""%1"".'"),
		ВерсияМетаданных);
	КонецЕсли;
	ЗаписатьИнформацию(Сообщение);
	
	// Установка блокировки информационной базы.
	БлокировкаУстановленаРанее = ПараметрыОбновления.УстановленнаяБлокировкаИБ <> Неопределено 
		И ПараметрыОбновления.УстановленнаяБлокировкаИБ.Установлена;
	Если БлокировкаУстановленаРанее Тогда
		ИтерацииОбновления = ИтерацииОбновления();
		БлокировкаИБ = ПараметрыОбновления.УстановленнаяБлокировкаИБ;
	Иначе
		БлокировкаИБ = Неопределено;
		ИтерацииОбновления = ЗаблокироватьИБ(БлокировкаИБ, ПараметрыОбновления.ИсключениеПриНевозможностиБлокировкиИБ);
		Если БлокировкаИБ.Ошибка <> Неопределено Тогда
			Результат.Возврат = БлокировкаИБ.Ошибка;
			Возврат Результат;
		КонецЕсли;
	КонецЕсли;
	
	Результат.Вставить("РежимОбновленияДанных", РежимОбновленияДанных);
	Результат.Вставить("ИтерацииОбновления", ИтерацииОбновления);
	Результат.Вставить("БлокировкаИБ", БлокировкаИБ);
	Результат.Вставить("БлокировкаУстановленаРанее", БлокировкаУстановленаРанее);
	Результат.Вставить("ВерсияДанных", ВерсияДанных);
	Результат.Вставить("ВерсияМетаданных", ВерсияМетаданных);
	
	Возврат Результат;
	
КонецФункции

Процедура ВыполнитьДействияПриОбновленииИнформационнойБазы(ПараметрыОбновления, ДополнительныеПараметры)
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		// Установка привилегированного режима для возможности обновления ИБ в модели сервиса,
		// в случае когда администратор области данных выполняет вход в область до завершения обновления области.
		Если ОбщегоНазначения.РазделениеВключено() И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
			УстановитьПривилегированныйРежим(Истина);
		КонецЕсли;
	КонецЕсли;
	
	РежимОбновленияДанных = ДополнительныеПараметры.РежимОбновленияДанных;
	ИтерацииОбновления    = ДополнительныеПараметры.ИтерацииОбновления;
	БлокировкаИБ          = ДополнительныеПараметры.БлокировкаИБ;
	БлокировкаУстановленаРанее = ДополнительныеПараметры.БлокировкаУстановленаРанее;
	РежимВыполненияОтложенногоОбновления = ДополнительныеПараметры.РежимВыполненияОтложенногоОбновления;
	ВыполнитьОтложенноеОбновлениеСейчас  = ДополнительныеПараметры.ВыполнитьОтложенноеОбновлениеСейчас;
	
	ОперативноеОбновление = БлокировкаИБ.ОперативноеОбновление;
	
	Попытка
		
		Если РежимОбновленияДанных = "ПереходСДругойПрограммы" Тогда
			ПерейтиСДругойПрограммы(ИтерацииОбновления);
			ИтерацииОбновления = ИтерацииОбновления();
			
			РежимОбновленияДанных = РежимОбновленияДанных();
			ОперативноеОбновление = Ложь;
		КонецЕсли;
		
	Исключение
		Если Не БлокировкаУстановленаРанее Тогда
			РазблокироватьИБ(БлокировкаИБ);
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;
	
	ОбновлениеИнформационнойБазыПереопределяемый.ПередФормированиеСпискаОтложенныхОбработчиков(ИтерацииОбновления);
	Обработки.ОписаниеОбработчиковОбновления.ЗаполнитьНомерОчереди(ИтерацииОбновления);
	ОбновитьСписокВыполняемыхОбработчиковОбновления(ИтерацииОбновления);
	СброситьПрогрессВыполненияОбработчиков();
	ОчиститьЗарегистрированныеПроблемыСДанными();
	
	Если ПараметрыОбновления.ВФоне Тогда
		ДлительныеОперации.СообщитьПрогресс(10);
	КонецЕсли;
	
	Параметры = Новый Структура;
	Параметры.Вставить("ХодВыполненияОбработчиков", КоличествоОбработчиковНаТекущуюВерсию(ИтерацииОбновления, РежимВыполненияОтложенногоОбновления));
	Параметры.Вставить("ОперативноеОбновление", ОперативноеОбновление);
	Параметры.Вставить("ВФоне", ПараметрыОбновления.ВФоне);
	Параметры.Вставить("ПриЗапускеКлиентскогоПриложения", ПараметрыОбновления.ПриЗапускеКлиентскогоПриложения);
	Параметры.Вставить("РежимВыполненияОтложенногоОбновления", РежимВыполненияОтложенногоОбновления);
	
	Сообщение = НСтр("ru = 'Для обновления программы на новую версию будут выполнены обработчики: %1'");
	Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Сообщение, Параметры.ХодВыполненияОбработчиков.ВсегоОбработчиков);
	ЗаписатьИнформацию(Сообщение);
	
	Попытка
		
		// Выполняем все обработчики обновления для подсистем конфигурации.
		Для Каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
			ИтерацияОбновления.ВыполненныеОбработчики = ВыполнитьИтерациюОбновления(ИтерацияОбновления, Параметры); // @skip-check query-in-loop - выполнение монопольных и оперативных обработчиков.
		КонецЦикла;
		
		// Очистка списка новых подсистем.
		СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
		СведенияОбОбновлении.НовыеПодсистемы = Новый Массив;
		СведенияОбОбновлении.ВсеНовыеПодсистемы = Новый Массив;
		ЗаполнитьДанныеДляПараллельногоОтложенногоОбновления(Параметры);
		ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
		
		// Для файловой базы и при обновлении скриптом на насколько версий
		// отложенные обработчики выполняются в основном цикле обновления.
		Если ВыполнитьОтложенноеОбновлениеСейчас Тогда
			ВыполнитьОтложенноеОбновлениеСейчас(Параметры);
		КонецЕсли;
		
	Исключение
		Если Не БлокировкаУстановленаРанее Тогда
			РазблокироватьИБ(БлокировкаИБ);
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;

КонецПроцедуры

Процедура ВыполнитьДействияПослеОбновленияИнформационнойБазы(ПараметрыОбновления, ДополнительныеПараметры)
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		// Установка привилегированного режима для возможности обновления ИБ в модели сервиса,
		// в случае когда администратор области данных выполняет вход в область до завершения обновления области.
		Если ОбщегоНазначения.РазделениеВключено() И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
			УстановитьПривилегированныйРежим(Истина);
		КонецЕсли;
	КонецЕсли;
	
	РежимОбновленияДанных = ДополнительныеПараметры.РежимОбновленияДанных;
	ИтерацииОбновления    = ДополнительныеПараметры.ИтерацииОбновления;
	БлокировкаИБ          = ДополнительныеПараметры.БлокировкаИБ;
	ОперативноеОбновление = ДополнительныеПараметры.БлокировкаИБ.ОперативноеОбновление;
	ВерсияДанных          = ДополнительныеПараметры.ВерсияДанных;
	ВерсияМетаданных      = ДополнительныеПараметры.ВерсияМетаданных;
	БлокировкаУстановленаРанее           = ДополнительныеПараметры.БлокировкаУстановленаРанее;
	РежимВыполненияОтложенногоОбновления = ДополнительныеПараметры.РежимВыполненияОтложенногоОбновления;
	ВыполнитьОтложенноеОбновлениеСейчас  = ДополнительныеПараметры.ВыполнитьОтложенноеОбновлениеСейчас;
	
	// Отключение монопольного режима.
	Если Не БлокировкаУстановленаРанее Тогда
		РазблокироватьИБ(БлокировкаИБ);
	КонецЕсли;
	
	Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Обновление информационной базы на версию ""%1"" выполнено успешно.'"), ВерсияМетаданных);
		ЗаписатьИнформацию(Сообщение);
	
	ВыводитьОписаниеОбновлений = (РежимОбновленияДанных <> "НачальноеЗаполнение");
	
	ОбновитьПовторноИспользуемыеЗначения();
	
	// После обновления информационной базы.
	//
	ВыполнитьОбработчикиПослеОбновленияИнформационнойБазы(
		ИтерацииОбновления,
		Константы.ДетализироватьОбновлениеИБВЖурналеРегистрации.Получить(),
		ВыводитьОписаниеОбновлений,
		ОперативноеОбновление);
	
	ОбновлениеИнформационнойБазыПереопределяемый.ПослеОбновленияИнформационнойБазы(
		ВерсияДанных,
		ВерсияМетаданных,
		ИтерацииОбновления,
		ВыводитьОписаниеОбновлений,
		Не ОперативноеОбновление);
	
	// Выгрузка сообщения обмена после перезапуска в связи с получением изменений конфигурации.
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
		МодульОбменДаннымиСервер.ПослеОбновленияИнформационнойБазы();
	КонецЕсли;
	
	// Для клиент-серверной базы запланировать выполнение отложенных обработчиков обновления.
	Если РежимВыполненияОтложенногоОбновления <> Неопределено
		И РежимВыполненияОтложенногоОбновления = "Отложенно" Тогда
		ЗапланироватьОтложенноеОбновление();
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено() И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		РегламентныеЗаданияСервер.УстановитьИспользованиеРегламентногоЗадания(Метаданные.РегламентныеЗадания.УстановкаПорядкаОтложенногоОбновленияВМоделиСервиса, Истина);
	КонецЕсли;
	
	ОпределитьВыводОписанияОбновлений(ВыводитьОписаниеОбновлений);
	
	// Сброс неуспешного статуса обновления конфигурации при завершении обновления вручную (без скрипта).
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбновлениеКонфигурации") Тогда
		МодульОбновлениеКонфигурации = ОбщегоНазначения.ОбщийМодуль("ОбновлениеКонфигурации");
		МодульОбновлениеКонфигурации.ПослеОбновленияИнформационнойБазы();
	КонецЕсли;
	
	ОбновитьПовторноИспользуемыеЗначения();
	
	Если УстановленоЗапуститьОбновлениеИнформационнойБазы() Тогда
		УстановитьПривилегированныйРежим(Истина);
		СтандартныеПодсистемыСервер.ЗарегистрироватьИзменениеПриоритетныхДанныхДляПодчиненныхУзловРИБ();
		УстановитьПривилегированныйРежим(Ложь);
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступомСлужебный = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступомСлужебный");
		МодульУправлениеДоступомСлужебный.ОбновитьДоступПослеОбновленияИнформационнойБазы(
		ВыполнитьОтложенноеОбновлениеСейчас);
	КонецЕсли;
	
	Если Не ПараметрыОбновления.ПриЗапускеКлиентскогоПриложения Тогда
		УстановитьЗапускОбновленияИнформационнойБазы(Ложь);
		ПараметрыСеанса.ВыполняетсяОбновлениеИБ = Ложь;
	КонецЕсли;
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		НастроитьЗаданиеОчисткаУстаревшихДанных(Истина);
	КонецЕсли;
	
КонецПроцедуры

Процедура ВыполнитьДействияПослеОтложенногоОбновленияИнформационнойБазы(СинхронноеОбновление = Ложь)
	
	Если Не ОтложенноеОбновлениеЗавершено() Тогда
		Возврат;
	КонецЕсли;
	
	Если СинхронноеОбновление Тогда
		ОчиститьПолностьюПослеУспешногоОтложенногоОбновления();
	Иначе
		НастроитьЗаданиеОчисткаУстаревшихДанных(Истина);
	КонецЕсли;
	
КонецПроцедуры

// Возвращает признак разрешения использования многопоточного обновления.
// Многопоточное обновление можно включить в ОбновлениеИнформационнойБазыПереопределяемый.ПриОпределенииНастроек().
//
// Возвращаемое значение:
//  Булево - если Истина, многопоточное обновление разрешено. По умолчанию - Ложь (для обратной совместимости).
//
Функция РазрешеноМногопоточноеОбновление() Экспорт
	
	Параметры = НастройкиПодсистемы();
	Возврат Параметры.МногопоточноеОбновление;
	
КонецФункции

// Параметры:
//  ИмяПараметра - Строка
//  УстановленныеПараметры - Массив из Строка
//
Процедура УстановкаПараметровСеанса(Знач ИмяПараметра, УстановленныеПараметры) Экспорт
	
	Если ИмяПараметра = "ВыполняетсяОбновлениеИБ" Тогда
		ПараметрыСеанса.ВыполняетсяОбновлениеИБ = ОбновлениеИнформационнойБазы.НеобходимоОбновлениеИнформационнойБазы();
		УстановленныеПараметры.Добавить("ВыполняетсяОбновлениеИБ");
	ИначеЕсли ИмяПараметра = "ПараметрыОбработчикаОбновления" Тогда
		ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(НовыеПараметрыОбработчикаОбновления());
		УстановленныеПараметры.Добавить("ПараметрыОбработчикаОбновления");
	КонецЕсли;
	
КонецПроцедуры

// Возвращает числовой вес версии для сравнения версий между собой.
//
// Параметры:
//  Версия - Строка - версия в строковом формате.
//
// Возвращаемое значение:
//  Число - вес версии
//
Функция ВесВерсии(Знач Версия) Экспорт
	
	Если Версия = "" Тогда
		Возврат 0;
	КонецЕсли;
	
	Если СтрНачинаетсяС(Версия, "ОтладкаОбработчика") Тогда
		Возврат 99000000000;
	КонецЕсли;
	
	Возврат ВесВерсииИзМассиваСтрок(СтрРазделить(Версия, "."));
	
КонецФункции

// Для внутреннего использования.
//
// Параметры:
//  ИмяКонфигурацииИлиБиблиотеки - Строка
//  Версия - Строка
//  Обработчики - см. ОбновлениеИнформационнойБазы.НоваяТаблицаОбработчиковОбновления
//  ЭтоОсновнаяКонфигурация - Булево
//                          - Неопределено
// Возвращаемое значение:
//  Структура:
//     * ПредыдущаяВерсия - Строка
//     * ОсновнойСерверныйМодуль - Строка
//     * ИмяОсновногоСерверногоМодуля - Строка
//     * ВыполненныеОбработчики - Неопределено
//     * Обработчики - ТаблицаЗначений:
//       ** НачальноеЗаполнение - Булево
//       ** Версия - Строка
//       ** Процедура - Строка
//       ** РежимВыполнения - Строка
//       ** ОбщиеДанные - Булево
//       ** УправлениеОбработчиками - Булево
//       ** Комментарий - Строка
//       ** Идентификатор - УникальныйИдентификатор
//       ** БлокируемыеОбъекты - Строка
//       ** ПроцедураПроверки - Строка
//       ** ПроцедураЗаполненияДанныхОбновления - Строка
//       ** ЗапускатьТолькоВГлавномУзле - Булево
//       ** ЗапускатьИВПодчиненномУзлеРИБСФильтрами - Булево
//       ** ЧитаемыеОбъекты - Строка
//       ** ИзменяемыеОбъекты - Строка
//       ** ПриоритетыВыполнения - ТаблицаЗначений
//       ** ВыполнятьВГруппеОбязательных - Булево
//       ** Приоритет - Число
//       ** МонопольныйРежим - Неопределено
//                           - Булево
//     * ЭтоОсновнаяКонфигурация - Булево
//                               - Неопределено
//
Функция ИтерацияОбновления(ИмяКонфигурацииИлиБиблиотеки, Версия, Обработчики, ЭтоОсновнаяКонфигурация = Неопределено) Экспорт
	
	ИтерацияОбновления = Новый Структура;
	ИтерацияОбновления.Вставить("Подсистема",  ИмяКонфигурацииИлиБиблиотеки);
	ИтерацияОбновления.Вставить("Версия",      Версия);
	ИтерацияОбновления.Вставить("ЭтоОсновнаяКонфигурация", 
		?(ЭтоОсновнаяКонфигурация <> Неопределено, ЭтоОсновнаяКонфигурация, ИмяКонфигурацииИлиБиблиотеки = Метаданные.Имя));
	ИтерацияОбновления.Вставить("Обработчики", Обработчики);
	ИтерацияОбновления.Вставить("ВыполненныеОбработчики", Неопределено);
	ИтерацияОбновления.Вставить("ИмяОсновногоСерверногоМодуля", "");
	ИтерацияОбновления.Вставить("ОсновнойСерверныйМодуль", "");
	ИтерацияОбновления.Вставить("ПредыдущаяВерсия", "");
	Возврат ИтерацияОбновления;
	
КонецФункции

// Проверить права текущего пользователя на выполнение обновления информационной базы.
Функция ЕстьПраваНаОбновлениеИнформационнойБазы(УчитыватьПривилегированныйРежим = Истина,
			РазделенныеДанные = Неопределено, УпрощеннаяФормаОбновленияИБ = Ложь)
	
	ПроверятьПраваАдминистрированияСистемы = Истина;
	
	Если РазделенныеДанные = Неопределено Тогда
		РазделенныеДанные = НЕ ОбщегоНазначения.РазделениеВключено()
			ИЛИ ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено()
	   И РазделенныеДанные Тогда
		
		Если НЕ ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
			Возврат Ложь;
		КонецЕсли;
		ПроверятьПраваАдминистрированияСистемы = Ложь;
	КонецЕсли;
	
	ЕстьПрава = Пользователи.ЭтоПолноправныйПользователь(
		, ПроверятьПраваАдминистрированияСистемы, УчитыватьПривилегированныйРежим);
	
	Если ЕстьПрава Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Не ПроверятьПраваАдминистрированияСистемы Тогда
		УпрощеннаяФормаОбновленияИБ = Истина;
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Для внутреннего использования.
//
Функция ОбновитьИнформационнуюБазуВФоне(УникальныйИдентификаторФормы, БлокировкаИБ) Экспорт
	
	// Запуск фонового задания
	ПараметрыОбновленияИБ = Новый Структура;
	ПараметрыОбновленияИБ.Вставить("ИсключениеПриНевозможностиБлокировкиИБ", Ложь);
	ПараметрыОбновленияИБ.Вставить("БлокировкаИБ", БлокировкаИБ);
	ПараметрыОбновленияИБ.Вставить("ПараметрыКлиентаНаСервере", СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере());
	
	// Установка монопольного режима до запуска фонового выполнения обновления.
	Попытка
		ЗаблокироватьИБ(ПараметрыОбновленияИБ.БлокировкаИБ, Ложь);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		
		Результат = Новый Структура;
		Результат.Вставить("Статус",    "Ошибка");
		Результат.Вставить("БлокировкаИБ", ПараметрыОбновленияИБ.БлокировкаИБ);
		Результат.Вставить("КраткоеПредставлениеОшибки", ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
		Результат.Вставить("ПодробноеПредставлениеОшибки", ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
		
		Возврат Результат;
	КонецПопытки;
	
	ПараметрыОбновленияИБ.Вставить("ВФоне", Не ПараметрыОбновленияИБ.БлокировкаИБ.РежимОтладки);
	
	Если Не ПараметрыОбновленияИБ.ВФоне Тогда
		ПараметрыОбновленияИБ.Удалить("ПараметрыКлиентаНаСервере");
	КонецЕсли;
	
	ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификаторФормы);
	ПараметрыВыполнения.ОжидатьЗавершение = 0;
	ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Фоновое обновление информационной базы'");
	// Обновление должно выполняться в фоне для возможности видеть прогресс выполнения.
	// В режиме обновления запуск всех фоновых заданий прерывается специальной вставкой кода, поэтому
	// в файловом режиме заметных задержек запуска не будет даже при отсутствии монопольного режима.
	ПараметрыВыполнения.ЗапуститьВФоне = Истина;
	
	Результат = ДлительныеОперации.ВыполнитьВФоне("ОбновлениеИнформационнойБазыСлужебный.ВыполнитьОбновлениеИнформационнойБазыВФоне",
		ПараметрыОбновленияИБ, ПараметрыВыполнения);
	
	Результат.Вставить("БлокировкаИБ", ПараметрыОбновленияИБ.БлокировкаИБ);
	
	// Если обновление ИБ уже выполнилось - разблокируем ИБ.
	Если Результат.Статус <> "Выполняется" Тогда
		РазблокироватьИБ(ПараметрыОбновленияИБ.БлокировкаИБ);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Запускает обновление информационной базы в длительной операции.
Процедура ВыполнитьОбновлениеИнформационнойБазыВФоне(ПараметрыОбновленияИБ, АдресХранилища) Экспорт
	
	ИнформацияОбОшибке = Неопределено;
	Попытка
		ПараметрыОбновления = ПараметрыОбновления();
		ПараметрыОбновления.ИсключениеПриНевозможностиБлокировкиИБ = ПараметрыОбновленияИБ.ИсключениеПриНевозможностиБлокировкиИБ;
		ПараметрыОбновления.ПриЗапускеКлиентскогоПриложения = Истина;
		ПараметрыОбновления.Перезапустить = Ложь;
		ПараметрыОбновления.УстановленнаяБлокировкаИБ = ПараметрыОбновленияИБ.БлокировкаИБ;
		ПараметрыОбновления.ВФоне = ПараметрыОбновленияИБ.ВФоне;
		
		Результат = ВыполнитьОбновлениеИнформационнойБазы(ПараметрыОбновления);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		// Переход в режим открытия формы повторной синхронизации данных перед запуском
		// с двумя вариантами "Синхронизировать и продолжить" и "Продолжить".
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными")
		   И ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ() Тогда
			МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
			МодульОбменДаннымиСервер.ВключитьПовторениеЗагрузкиСообщенияОбменаДаннымиПередЗапуском();
		КонецЕсли;
	КонецПопытки;
	
	Если ИнформацияОбОшибке <> Неопределено Тогда
		РезультатОбновления = Новый Структура;
		РезультатОбновления.Вставить("КраткоеПредставлениеОшибки", ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
		РезультатОбновления.Вставить("ПодробноеПредставлениеОшибки", ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
	ИначеЕсли Не ПараметрыОбновленияИБ.ВФоне Тогда
		РезультатОбновления = Результат;
	Иначе
		РезультатОбновления = Новый Структура;
		РезультатОбновления.Вставить("Результат", Результат);
	КонецЕсли;
	ПоместитьВоВременноеХранилище(РезультатОбновления, АдресХранилища);
	
КонецПроцедуры

Функция ОбновлениеПодОграниченнымиПравами(БлокировкаИБ) Экспорт
	Если Не ОбщегоНазначения.РазделениеВключено()
		Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ПараметрыОбновления = ПараметрыОбновления();
	ПараметрыОбновления.ИсключениеПриНевозможностиБлокировкиИБ = Ложь;
	ПараметрыОбновления.УстановленнаяБлокировкаИБ = БлокировкаИБ;
	
	Результат = "";
	// АПК:280 -выкл ошибки пишутся в журнал регистрации, пользователю не показываются.
	Попытка
		УстановитьПривилегированныйРежим(Истина);
		Результат = ВыполнитьОбновлениеИнформационнойБазы(ПараметрыОбновления);
		УстановитьПривилегированныйРежим(Ложь);
	Исключение
		// Обработка исключения не требуется.
	КонецПопытки;
	// АПК:280 -вкл.
	
	Возврат (Результат = "Успешно" Или Результат = "НеТребуется");
КонецФункции

// Для внутреннего использования.
//
Функция ЗаблокироватьИБ(БлокировкаИБ, ИсключениеПриНевозможностиБлокировкиИБ) Экспорт
	
	ИтерацииОбновления = Неопределено;
	Если БлокировкаИБ = Неопределено Тогда
		БлокировкаИБ = БлокировкаИБ();
	КонецЕсли;
	
	БлокировкаИБ.Установлена = Истина;
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		БлокировкаИБ.РежимОтладки = Ложь;
	Иначе
		БлокировкаИБ.РежимОтладки = ОбщегоНазначения.РежимОтладки();
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		БлокировкаИБ.КлючЗаписи = МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ЗаблокироватьВерсииОбластиДанных();
	КонецЕсли;
	
	ИтерацииОбновления = ИтерацииОбновления();
	БлокировкаИБ.ОперативноеОбновление = Ложь;
	
	Если БлокировкаИБ.РежимОтладки Тогда
		Возврат ИтерацииОбновления;
	КонецЕсли;
	
	// Установка монопольного режима для обновления информационной базы.
	ИнформацияОбОшибке = Неопределено;
	Попытка
		Если НЕ МонопольныйРежим() Тогда
			УстановитьМонопольныйРежим(Истина);
		КонецЕсли;
		Возврат ИтерацииОбновления;
	Исключение
		Если ВозможноОперативноеОбновление(ИтерацииОбновления) Тогда
			БлокировкаИБ.ОперативноеОбновление = Истина;
			Возврат ИтерацииОбновления;
		КонецЕсли;
		ИнформацияОбОшибке = ИнформацияОбОшибке();
	КонецПопытки;
	
	// Обработка неудачной попытки установки монопольного режима.
	Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Невозможно выполнить обновление информационной базы:
			|- Невозможно установить монопольный режим
			|- Версия конфигурации не предусматривает обновление без установки монопольного режима
			|
			|Подробности ошибки:
			|%1'"),
		ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
	
	ЗаписатьОшибку(Сообщение);
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.РазблокироватьВерсииОбластиДанных(БлокировкаИБ.КлючЗаписи);
	КонецЕсли;
	
	Если Не ИсключениеПриНевозможностиБлокировкиИБ
	   И ОбщегоНазначения.ИнформационнаяБазаФайловая()
	   И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ЗавершениеРаботыПользователей") Тогда
		
		ПараметрЗапускаКлиента = СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере().Получить("ПараметрЗапуска");
		Если СтрНайти(ПараметрЗапускаКлиента, "РегламентныеЗаданияОтключены") = 0 Тогда
			БлокировкаИБ.Ошибка = "ЗаблокироватьВыполнениеРегламентныхЗаданий";
		Иначе
			БлокировкаИБ.Ошибка = "ОшибкаУстановкиМонопольногоРежима";
		КонецЕсли;
	КонецЕсли;
	
	ВызватьИсключение Сообщение;
	
КонецФункции

// Для внутреннего использования.
//
Процедура РазблокироватьИБ(БлокировкаИБ) Экспорт
	
	Если БлокировкаИБ.РежимОтладки Тогда
		Возврат;
	КонецЕсли;
		
	Если МонопольныйРежим() Тогда
		Пока ТранзакцияАктивна() Цикл
			ОтменитьТранзакцию(); // АПК:325 Отмена незакрытых транзакций.
		КонецЦикла;
		
		УстановитьМонопольныйРежим(Ложь);
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.РазблокироватьВерсииОбластиДанных(БлокировкаИБ.КлючЗаписи);
	КонецЕсли;
	
КонецПроцедуры

// Для внутреннего использования.
//
Функция БлокировкаИБ()
	
	Результат = Новый Структура;
	Результат.Вставить("Установлена", Ложь);
	Результат.Вставить("Ошибка", Неопределено);
	Результат.Вставить("ОперативноеОбновление", Неопределено);
	Результат.Вставить("КлючЗаписи", Неопределено);
	Результат.Вставить("РежимОтладки", Неопределено);
	Возврат Результат;
	
КонецФункции

// Для внутреннего использования.
//
Функция ПараметрыОбновления() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ИсключениеПриНевозможностиБлокировкиИБ", Истина);
	Результат.Вставить("ПриЗапускеКлиентскогоПриложения", Ложь);
	Результат.Вставить("Перезапустить", Ложь);
	Результат.Вставить("УстановленнаяБлокировкаИБ", Неопределено);
	Результат.Вставить("ВФоне", Ложь);
	Результат.Вставить("ВыполнятьОтложенныеОбработчики", Ложь);
	Возврат Результат;
	
КонецФункции

// Для внутреннего использования.
//
Функция НоваяТаблицаОбработчиковПереходаСДругойПрограммы()
	
	Обработчики = Новый ТаблицаЗначений;
	Обработчики.Колонки.Добавить("ПредыдущееИмяКонфигурации", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(0)));
	Обработчики.Колонки.Добавить("Процедура",                 Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(0)));
	Обработчики.Колонки.Добавить("ОбщиеДанные",               Новый ОписаниеТипов("Булево"));
	Возврат Обработчики;
	
КонецФункции

// Для внутреннего использования.
//
Функция ОбработчикиПереходаСДругойПрограммы(ПредыдущееИмяКонфигурации, ИтерацииОбновления) 
	
	ОбработчикиПерехода = НоваяТаблицаОбработчиковПереходаСДругойПрограммы();
	ИмяОсновнойКонфигурации = Метаданные.Имя;
	
	// Начальное заполнение для объектов новых подсистем.
	ОбработчикиПерехода = НоваяТаблицаОбработчиковПереходаСДругойПрограммы();
	ИмяОсновнойКонфигурации = Метаданные.Имя;
	
	ОписанияПодсистем = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем();
	ПодсистемыКонфигурации = ОписанияПодсистем.ПоИменам;
	ВсеНовыеПодсистемы = СведенияОбОбновленииИнформационнойБазы().ВсеНовыеПодсистемы;
	
	// Добавление обработчика заполнения предопределенных данных.
	ОбработчикПерехода = ОбработчикиПерехода.Добавить();
	ОбработчикПерехода.ПредыдущееИмяКонфигурации = "*";
	ОбработчикПерехода.ОбщиеДанные = Ложь;
	ОбработчикПерехода.Процедура   = ОбработчикНачальногоЗаполненияПредопределенных();
	
	НовыеОбъектыМетаданных = Новый Массив;
	Для Каждого Подсистема Из ВсеНовыеПодсистемы Цикл
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Подсистема);
		Если ОбъектМетаданных = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		Если ОбъектМетаданных.ВключатьВКомандныйИнтерфейс Тогда
			Продолжить;
		КонецЕсли;
		
		Для Каждого ОбъектПодсистемы Из ОбъектМетаданных.Состав Цикл
			НовыеОбъектыМетаданных.Добавить(ОбъектПодсистемы.ПолноеИмя());
		КонецЦикла;
	КонецЦикла;
	
	Для Каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
		
		ОписаниеПодсистемы = ПодсистемыКонфигурации.Получить(ИтерацияОбновления.Подсистема);
		Если Не ОписаниеПодсистемы.ЗаполнятьДанныеНовыхПодсистемПриПереходеСДругойПрограммы Тогда
			Продолжить;
		КонецЕсли;
		
		Отбор = Новый Структура;
		Отбор.Вставить("НачальноеЗаполнение", Истина);
		
		ОбработчикиНачальногоЗаполнения = ИтерацияОбновления.Обработчики.НайтиСтроки(Отбор);
		Для Каждого Обработчик Из ОбработчикиНачальногоЗаполнения Цикл
			Если Обработчик.РежимВыполнения = "Отложенно"
				Или Обработчик.НеВыполнятьПриПереходеСДругойПрограммы Тогда
				Продолжить;
			КонецЕсли;
			
			Позиция      = СтрНайти(Обработчик.Процедура, ".", НаправлениеПоиска.СКонца);
			ИмяМенеджера = Лев(Обработчик.Процедура, Позиция - 1);
			ПолноеИмяОбъекта = ИмяОбъектаМетаданныхПоИмениМенеджера(ИмяМенеджера);
			
			Если НовыеОбъектыМетаданных.Найти(ПолноеИмяОбъекта) <> Неопределено Тогда
				ОбработчикПерехода = ОбработчикиПерехода.Добавить();
				ОбработчикПерехода.ПредыдущееИмяКонфигурации = "*";
				ОбработчикПерехода.ОбщиеДанные = Обработчик.ОбщиеДанные;
				ОбработчикПерехода.Процедура   = Обработчик.Процедура;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	// Добавление штатных обработчиков перехода.
	ОписанияПодсистем  = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем();
	Для каждого ИмяПодсистемы Из ОписанияПодсистем.Порядок Цикл
		ОписаниеПодсистемы = ОписанияПодсистем.ПоИменам.Получить(ИмяПодсистемы); // см. СтандартныеПодсистемыПовтИсп.НовоеОписаниеПодсистемы
		Если НЕ ЗначениеЗаполнено(ОписаниеПодсистемы.ОсновнойСерверныйМодуль) Тогда
			Продолжить;
		КонецЕсли;
		
		Если ОписаниеПодсистемы.Имя <> ИмяОсновнойКонфигурации Тогда
			Продолжить;
		КонецЕсли;
		
		Модуль = ОбщегоНазначения.ОбщийМодуль(ОписаниеПодсистемы.ОсновнойСерверныйМодуль);
		Модуль.ПриДобавленииОбработчиковПереходаСДругойПрограммы(ОбработчикиПерехода);
	КонецЦикла;
	// Дополнительно добавляются обязательные обработчики перехода БСП.
	ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковПереходаСДругойПрограммы(ОбработчикиПерехода);
	
	Отбор = Новый Структура("ПредыдущееИмяКонфигурации", "*");
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Отбор.Вставить("ОбщиеДанные", Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных());
	КонецЕсли;
	ВыбранныеОбработчики = ОбработчикиПерехода.НайтиСтроки(Отбор);
	
	Отбор.ПредыдущееИмяКонфигурации = ПредыдущееИмяКонфигурации;
	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ВыбранныеОбработчики, ОбработчикиПерехода.НайтиСтроки(Отбор), Истина);
	
	Результат = Новый Структура;
	Результат.Вставить("Обработчики", ВыбранныеОбработчики);
	Результат.Вставить("НовыеОбъекты", НовыеОбъектыМетаданных);
	
	Возврат Результат;
	
КонецФункции

Процедура ПерейтиСДругойПрограммы(ИтерацииОбновления)
	
	ЭтоНеразделенныйСеанс = Не ОбщегоНазначения.РазделениеВключено()
		Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	
	МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = Неопределено;
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
	КонецЕсли;
	
	// Предыдущее имя конфигурации, с которой нужно выполнить переход.
	Если ЭтоНеразделенныйСеанс Тогда
		Запрос = Новый Запрос;
		Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ВерсииПодсистем.ИмяПодсистемы КАК ИмяПодсистемы,
		|	ВерсииПодсистем.Версия КАК Версия
		|ИЗ
		|	РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем
		|ГДЕ
		|	ВерсииПодсистем.ЭтоОсновнаяКонфигурация = ИСТИНА";
		// АПК:1328-выкл Нет конкурентной работы, заполнение признака основной конфигурации выполняется
		// только сеансом выполняющим обновления и который может быть только один.
		РезультатЗапроса = Запрос.Выполнить();
		// АПК:1328-вкл
	ИначеЕсли МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = Неопределено Тогда
		// Отсутствует подсистема ОбновлениеВерсииИБВМоделиСервиса.
		Возврат;
	Иначе
		РезультатЗапроса = МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ОсновнаяКонфигурацияВОбластиДанных();
	КонецЕсли;
	// Если по каким-то причинам не отработал обработчик обновления ЗаполнитьРеквизитЭтоОсновнаяКонфигурация.
	Если РезультатЗапроса.Пустой() Тогда 
		Возврат;
	КонецЕсли;
	
	РезультатЗапроса = РезультатЗапроса.Выгрузить()[0];
	ПредыдущееИмяКонфигурации = РезультатЗапроса.ИмяПодсистемы;
	ПредыдущаяВерсияКонфигурации = РезультатЗапроса.Версия;
	
	// Проверка заполнения реквизита ЭтоОсновнаяКонфигурация в областях.
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ЗаполнитьПризнакЭтоОсновнаяКонфигурация(ПредыдущееИмяКонфигурации);
	Иначе
		ОписанияПодсистем = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем();
		ОписаниеПредыдущейПодсистемы = ОписанияПодсистем.ПоИменам.Получить(ПредыдущееИмяКонфигурации);
		Если ОписаниеПредыдущейПодсистемы = Неопределено Тогда
			// Предыдущая подсистема была удалена.
			Запрос = Новый Запрос;
			Запрос.УстановитьПараметр("ИмяБиблиотеки", ПредыдущееИмяКонфигурации);
			Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
			Запрос.Текст =
				"ВЫБРАТЬ
				|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика
				|ИЗ
				|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
				|ГДЕ
				|	ОбработчикиОбновления.ИмяБиблиотеки = &ИмяБиблиотеки
				|	И ОбработчикиОбновления.Статус <> &Статус";
			Результат = Запрос.Выполнить().Выгрузить();
			Для Каждого Строка Из Результат Цикл
				НаборЗаписей = РегистрыСведений.ОбработчикиОбновления.СоздатьНаборЗаписей();
				НаборЗаписей.Отбор.ИмяОбработчика.Установить(Строка.ИмяОбработчика);
				НаборЗаписей.Прочитать();
				
				НаборЗаписей[0].ИмяБиблиотеки = Метаданные.Имя;
				
				НаборЗаписей.Записать(); // АПК:1327 нет конкурентной работы с регистром
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	
	ОбработчикиИНовыеОбъекты = ОбработчикиПереходаСДругойПрограммы(ПредыдущееИмяКонфигурации, ИтерацииОбновления);
	Обработчики  = ОбработчикиИНовыеОбъекты.Обработчики;
	НовыеОбъекты = ОбработчикиИНовыеОбъекты.НовыеОбъекты;
	
	ПодсистемаСуществует = ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом");
	// Выполняем все обработчики перехода.
	Для Каждого Обработчик Из Обработчики Цикл
		
		ТранзакцияАктивнаНаНачалоВыполнения = ТранзакцияАктивна();
		ОтключитьОбновлениеКлючейДоступа(Истина, ПодсистемаСуществует);
		Попытка
			// Начальное заполнение предопределенных для новых объектов.
			Если Обработчик.Процедура = ОбработчикНачальногоЗаполненияПредопределенных() Тогда
				ПараметрыОбработчика = Новый Массив;
				ПараметрыОбработчика.Добавить(Ложь);
				ПараметрыОбработчика.Добавить(Новый Структура("НовыеОбъекты", НовыеОбъекты));
				ОбщегоНазначения.ВыполнитьМетодКонфигурации(Обработчик.Процедура, ПараметрыОбработчика);
			Иначе
				// Выполнение обработчиков перехода.
				ОбщегоНазначения.ВыполнитьМетодКонфигурации(Обработчик.Процедура);
			КонецЕсли;
			ОтключитьОбновлениеКлючейДоступа(Ложь, ПодсистемаСуществует);
		Исключение
			
			ОтключитьОбновлениеКлючейДоступа(Ложь, ПодсистемаСуществует);
			ИмяОбработчика = Обработчик.Процедура;
			ЗаписатьОшибку(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'При вызове обработчика перехода с другой программы
				           |""%1""
				           |произошла ошибка:
				           |""%2"".'"),
				ИмяОбработчика,
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())));
			
			ВызватьИсключение;
		КонецПопытки;
		ПроверитьВложеннуюТранзакцию(ТранзакцияАктивнаНаНачалоВыполнения, Обработчик.Процедура);
		
	КонецЦикла;
	
	Параметры = Новый Структура();
	Параметры.Вставить("ВыполнитьОбновлениеСВерсии", Истина);
	Параметры.Вставить("ВерсияКонфигурации", Метаданные.Версия);
	Параметры.Вставить("ОчиститьСведенияОПредыдущейКонфигурации", Истина);
	ПриЗавершенииПереходаСДругойПрограммы(ПредыдущееИмяКонфигурации, ПредыдущаяВерсияКонфигурации, Параметры);
	
	// Установка текущих имени и версии конфигурации.
	НачатьТранзакцию();
	Попытка
		Если Параметры.ОчиститьСведенияОПредыдущейКонфигурации Тогда
			Если ЭтоНеразделенныйСеанс Тогда
				НаборЗаписей = РегистрыСведений.ВерсииПодсистем.СоздатьНаборЗаписей();
			Иначе
				НаборЗаписей = МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ЗаписьВерсийПодсистемОбластейДанных();
			КонецЕсли;
			НаборЗаписей.Отбор.ИмяПодсистемы.Установить(ПредыдущееИмяКонфигурации);
			НаборЗаписей.Записать();
		КонецЕсли;
		
		Если ЭтоНеразделенныйСеанс Тогда
			НаборЗаписей = РегистрыСведений.ВерсииПодсистем.СоздатьНаборЗаписей();
		Иначе
			НаборЗаписей = МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ЗаписьВерсийПодсистемОбластейДанных();
		КонецЕсли;
		НаборЗаписей.Отбор.ИмяПодсистемы.Установить(Метаданные.Имя);
		
		ВерсияКонфигурации = Метаданные.Версия; 
		Если Параметры.ВыполнитьОбновлениеСВерсии Тогда
			ВерсияКонфигурации = Параметры.ВерсияКонфигурации;
		КонецЕсли;
		НоваяЗапись = НаборЗаписей.Добавить();
		НоваяЗапись.ИмяПодсистемы = Метаданные.Имя;
		НоваяЗапись.Версия = ВерсияКонфигурации;
		Если ЭтоНеразделенныйСеанс Тогда
			НоваяЗапись.ПланОбновления = Неопределено;
		КонецЕсли;
		НоваяЗапись.ЭтоОсновнаяКонфигурация = Истина;
		
		НаборЗаписей.Записать();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	ОбновитьПовторноИспользуемыеЗначения();
	
КонецПроцедуры

Функция ОбработчикНачальногоЗаполненияПредопределенных()
	
	Возврат "ОбновлениеИнформационнойБазыСлужебный.НачальноеЗаполнениеПредопределенныхДанных";
	
КонецФункции

Процедура ПриЗавершенииПереходаСДругойПрограммы(ПредыдущееИмяКонфигурации, ПредыдущаяВерсияКонфигурации, Параметры)
	
	ИмяКонфигурации = Метаданные.Имя;
	ОписанияПодсистем  = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем();
	Для каждого ИмяПодсистемы Из ОписанияПодсистем.Порядок Цикл
		ОписаниеПодсистемы = ОписанияПодсистем.ПоИменам.Получить(ИмяПодсистемы); // см. СтандартныеПодсистемыПовтИсп.НовоеОписаниеПодсистемы
		Если НЕ ЗначениеЗаполнено(ОписаниеПодсистемы.ОсновнойСерверныйМодуль) Тогда
			Продолжить;
		КонецЕсли;
		
		Если ОписаниеПодсистемы.Имя <> ИмяКонфигурации Тогда
			Продолжить;
		КонецЕсли;
		
		Модуль = ОбщегоНазначения.ОбщийМодуль(ОписаниеПодсистемы.ОсновнойСерверныйМодуль);
		Модуль.ПриЗавершенииПереходаСДругойПрограммы(ПредыдущееИмяКонфигурации, ПредыдущаяВерсияКонфигурации, Параметры);
	КонецЦикла;
	
КонецПроцедуры

Процедура ОбновлениеВерсииИБПередУдалениемСсылочногоОбъекта(Источник, Отказ) Экспорт
	// АПК:75-выкл проверка ОбменДанными.Загрузка не требуется, т.к. данное событие
	// должно выполняться всегда в процессе выполнения отложенного обновления.
	
	Если ПолучитьФункциональнуюОпцию("ОтложенноеОбновлениеЗавершеноУспешно")
		Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	Если Метаданные.ПланыОбмена.ОбновлениеИнформационнойБазы.Состав.Найти(Источник.Метаданные()) = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбновлениеИнформационнойБазы.Ссылка КАК Ссылка
		|ИЗ
		|	ПланОбмена.ОбновлениеИнформационнойБазы КАК ОбновлениеИнформационнойБазы
		|ГДЕ
		|	ОбновлениеИнформационнойБазы.ЭтотУзел = ЛОЖЬ";
	
	УстановитьПривилегированныйРежим(Истина);
	Узлы = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	ПланыОбмена.УдалитьРегистрациюИзменений(Узлы, Источник);
КонецПроцедуры

Процедура УпорядочитьВерсииОбработчиков(ОбработчикиПоВерсиям)
	ОбработчикиПоВерсиям.Колонки.Добавить("ВерсияПорядок", Новый ОписаниеТипов("Число"));
	Для Каждого СтрокаВерсия Из ОбработчикиПоВерсиям.Строки Цикл
		Версия = СтрокаВерсия.Версия;
		Если Версия = "*" Или Не ЗначениеЗаполнено(Версия) Тогда
			СтрокаВерсия.ВерсияПорядок = 0;
		ИначеЕсли СтрНачинаетсяС(Версия, "ОтладкаОбработчика") Тогда
			СтрокаВерсия.ВерсияПорядок = 99000000000;
		Иначе
			СтрокаВерсия.ВерсияПорядок = ВесВерсииИзМассиваСтрок(СтрРазделить(Версия, "."));
		КонецЕсли;
	КонецЦикла;
	
	ОбработчикиПоВерсиям.Строки.Сортировать("ВерсияПорядок Возр");
КонецПроцедуры

Процедура ДобавитьОбработчики(ИмяБиблиотеки, ОбработчикиПоВерсиям, ДобавленныеОбработчики)
	
	Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		НаборРазделенныхОбработчиков   = РегистрыСведений.ОбработчикиОбновления.СоздатьНаборЗаписей();
		НаборРазделенныхОбработчиков.Прочитать();
	КонецЕсли;
	Если Не ОбщегоНазначения.РазделениеВключено() Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		НаборНеразделенныхОбработчиков = РегистрыСведений.ОбработчикиОбновленияОбщихДанных.СоздатьНаборЗаписей();
		НаборНеразделенныхОбработчиков.Прочитать();
	КонецЕсли;
	
	Для Каждого Версия Из ОбработчикиПоВерсиям Цикл
		Для Каждого Обработчик Из Версия.Строки Цикл
			Если ДобавленныеОбработчики.Найти(Обработчик.ИмяОбработчика) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			Если Обработчик.ОбщиеДанные = Истина Тогда
				Запись = НаборНеразделенныхОбработчиков.Добавить();
			Иначе
				Запись = НаборРазделенныхОбработчиков.Добавить();
			КонецЕсли;
			
			ЗаполнитьЗначенияСвойств(Запись, Обработчик, , "РежимВыполнения");
			
			Если Не ЗначениеЗаполнено(Обработчик.РежимВыполнения)
				И (Обработчик.МонопольныйРежим = Истина Или Обработчик.МонопольныйРежим = Неопределено) Тогда
				РежимВыполнения = Перечисления.РежимыВыполненияОбработчиков.Монопольно;
			ИначеЕсли Не ЗначениеЗаполнено(Обработчик.РежимВыполнения) И Обработчик.МонопольныйРежим = Ложь Тогда
				РежимВыполнения = Перечисления.РежимыВыполненияОбработчиков.Оперативно;
			Иначе
				РежимВыполнения = Перечисления.РежимыВыполненияОбработчиков[Обработчик.РежимВыполнения];
			КонецЕсли;
			
			Запись.РежимВыполнения = РежимВыполнения;
			Запись.Статус = Перечисления.СтатусыОбработчиковОбновления.НеВыполнялся;
			Запись.ИмяБиблиотеки = ИмяБиблиотеки;
			
			ДобавленныеОбработчики.Добавить(Обработчик.ИмяОбработчика);
		КонецЦикла;
	КонецЦикла;
	
	// АПК:1327-выкл нет конкурентной работы с регистром
	Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		НаборРазделенныхОбработчиков.Записать();
	КонецЕсли;
	Если Не ОбщегоНазначения.РазделениеВключено() Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		НаборНеразделенныхОбработчиков.Записать();
	КонецЕсли;
	// АПК:1327-вкл
	
КонецПроцедуры

Процедура УстановитьСтатусОбработчика(ИмяОбработчика, Статус, ТекстОшибки = "")
	
	НаборЗаписей = Неопределено;
	Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		НаборЗаписей = РегистрыСведений.ОбработчикиОбновления.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.ИмяОбработчика.Установить(ИмяОбработчика);
		НаборЗаписей.Прочитать();
	КонецЕсли;
	
	ДоступноЧтениеНеразделенныхДанных = Не ОбщегоНазначения.РазделениеВключено()
		Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	
	Если ДоступноЧтениеНеразделенныхДанных И (НаборЗаписей = Неопределено Или НаборЗаписей.Количество() = 0) Тогда
		НаборЗаписей = РегистрыСведений.ОбработчикиОбновленияОбщихДанных.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.ИмяОбработчика.Установить(ИмяОбработчика);
		НаборЗаписей.Прочитать();
	КонецЕсли;
	
	Если НаборЗаписей.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Запись = НаборЗаписей[0];
	Запись.Статус = Перечисления.СтатусыОбработчиковОбновления[Статус];
	Запись.ИнформацияОбОшибке = ТекстОшибки;
	
	НаборЗаписей.Записать();
	
КонецПроцедуры

Процедура УстановитьСвойствоОбработчика(ИмяОбработчика, Свойство, Значение) Экспорт
	
	Свойства = Новый Структура;
	Свойства.Вставить(Свойство, Значение);
	УстановитьСвойстваОбработчика(ИмяОбработчика, Свойства);
	
КонецПроцедуры

Процедура УстановитьСвойстваОбработчика(ИмяОбработчика, Свойства)
	
	Если Свойства.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	НаборЗаписей = Неопределено;
	Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		НаборЗаписей = РегистрыСведений.ОбработчикиОбновления.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.ИмяОбработчика.Установить(ИмяОбработчика);
		НаборЗаписей.Прочитать();
	КонецЕсли;
	
	ДоступноЧтениеНеразделенныхДанных = Не ОбщегоНазначения.РазделениеВключено()
		Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	
	Если ДоступноЧтениеНеразделенныхДанных И (НаборЗаписей = Неопределено Или НаборЗаписей.Количество() = 0) Тогда
		НаборЗаписей = РегистрыСведений.ОбработчикиОбновленияОбщихДанных.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.ИмяОбработчика.Установить(ИмяОбработчика);
		НаборЗаписей.Прочитать();
	КонецЕсли;
	
	Если НаборЗаписей.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Запись = НаборЗаписей[0];
	Для Каждого Свойство Из Свойства Цикл
		Запись[Свойство.Ключ] = Свойство.Значение;
	КонецЦикла;
	
	НаборЗаписей.Записать();
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Протоколирование хода обновления.

// Возвращает строковую константу для формирования сообщений журнала регистрации.
//
// Возвращаемое значение:
//   Строка
//
Функция СобытиеЖурналаРегистрации() Экспорт
	
	Возврат НСтр("ru = 'Обновление информационной базы'", ОбщегоНазначения.КодОсновногоЯзыка());
	
КонецФункции

// Возвращает строковую константу для формирования сообщений журнала регистрации
// протоколирования хода выполнения обработчиков обновления.
//
// Возвращаемое значение:
//   Строка
//
Функция СобытиеЖурналаРегистрацииПротокол() Экспорт
	
	Возврат СобытиеЖурналаРегистрации() + "." + НСтр("ru = 'Протокол выполнения'", ОбщегоНазначения.КодОсновногоЯзыка());
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Описание обновлений

// Формирует табличный документ с описанием изменений в версиях,
// которые соответствуют переданному списку версий Разделы.
//
Функция ДокументОписаниеОбновлений(Знач Разделы) Экспорт
	
	ДокументОписаниеОбновлений = Новый ТабличныйДокумент();
	Если Разделы.Количество() = 0 Тогда
		Возврат ДокументОписаниеОбновлений;
	КонецЕсли;
	
	МакетОписаниеОбновлений = Метаданные.ОбщиеМакеты.Найти("ОписаниеИзмененийСистемы");
	Если МакетОписаниеОбновлений <> Неопределено Тогда
		МакетОписаниеОбновлений = ПолучитьОбщийМакет(МакетОписаниеОбновлений);
	Иначе
		Возврат Новый ТабличныйДокумент();
	КонецЕсли;
	
	Для Каждого Версия Из Разделы Цикл
		
		ВывестиОписаниеИзменений(Версия, ДокументОписаниеОбновлений, МакетОписаниеОбновлений);
		
	КонецЦикла;
	
	Возврат ДокументОписаниеОбновлений;
	
КонецФункции

// Возвращает массив версий больше последней отображавшейся версии,
// для которых есть описания изменений системы.
//
// Возвращаемое значение:
//  Массив - содержит строки с версиями.
//
Функция НеотображавшиесяРазделыОписанияИзменений() Экспорт
	
	Разделы = РазделыОписанияИзменений();
	
	ПоследняяВерсия = ПоследняяВерсияОтображенияИзмененийСистемы();
	
	Если ПоследняяВерсия = Неопределено Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	Возврат ПолучитьВерсииБольшеЗаданной(Разделы, ПоследняяВерсия);
	
КонецФункции

// Устанавливает флаг отображения описаний изменений версий по
// текущую версию включительно.
//
// Параметры:
//  ИмяПользователя - Строка - имя пользователя, для которого
//   необходимо установить флаг.
//
Процедура УстановитьФлагОтображенияОписанийПоТекущуюВерсию(Знач ИмяПользователя = Неопределено) Экспорт
	
	ОбщегоНазначения.ХранилищеОбщихНастроекСохранить("ОбновлениеИБ",
		"ПоследняяВерсияОтображенияИзмененийСистемы", Метаданные.Версия, , ИмяПользователя);
		
	Если ИмяПользователя = Неопределено И Пользователи.ЭтоПолноправныйПользователь() Тогда
		
		ОбщегоНазначения.ХранилищеОбщихНастроекУдалить("ОбновлениеИБ", "ВывестиОписаниеИзмененийДляАдминистратора", ИмяПользователя());
		
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Механизм отложенного обновления.

// Планирует выполнение отложенного обновления в клиент-серверной базе.
//
Процедура ЗапланироватьОтложенноеОбновление()
	
	// Планирование выполнения регламентного задания.
	// При работе в модели сервиса - добавляется регламентное задание в очередь.
	Если Не ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		ПриВключенииОтложенногоОбновления(Истина);
	КонецЕсли;
	
КонецПроцедуры

// Управляет процессом выполнения отложенных обработчиков обновления.
// 
Процедура ВыполнитьОтложенноеОбновление() Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(Метаданные.РегламентныеЗадания.ОтложенноеОбновлениеИБ);
	
	Если ОбновлениеИнформационнойБазыСлужебныйПовтИсп.НеобходимоОбновлениеИнформационнойБазы() Тогда
		Возврат;
	КонецЕсли;
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	
	Если СведенияОбОбновлении.ВремяОкончаниеОтложенногоОбновления <> Неопределено Тогда
		ОтключитьОтложенноеОбновление();
		ВыполнитьДействияПослеОтложенногоОбновленияИнформационнойБазы();
		Возврат;
	КонецЕсли;
	
	Если СведенияОбОбновлении.ВремяНачалаОтложенногоОбновления = Неопределено Тогда
		СведенияОбОбновлении.ВремяНачалаОтложенногоОбновления = ТекущаяДатаСеанса();
	КонецЕсли;
	Если ТипЗнч(СведенияОбОбновлении.НомерСеанса) <> Тип("СписокЗначений") Тогда
		СведенияОбОбновлении.НомерСеанса = Новый СписокЗначений;
	КонецЕсли;
	СведенияОбОбновлении.НомерСеанса.Добавить(НомерСеансаИнформационнойБазы());
	СведенияОбОбновлении.ДатаНачалаСеансаОбновления = ТекущаяДатаСеанса();
	ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
	
	// В сеансе регламентного задания проверка дат запрета изменения и загрузки отключается.
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ДатыЗапретаИзменения") Тогда
		МодульДатыЗапретаИзменения = ОбщегоНазначения.ОбщийМодуль("ДатыЗапретаИзменения");
		МодульДатыЗапретаИзменения.ОтключитьПроверкуДатЗапрета(Истина);
	КонецЕсли;
	
	ОбработчикиВыполнялись = Истина;
	Обработанные = Новый Массив;
	
	ДатаПоследнейПроверки = Неопределено;
	Попытка
		Если ФорсироватьОбновление(СведенияОбОбновлении) Тогда
			Группы = НовоеОписаниеГруппПотоковОбработчиковОтложенногоОбновления();
			ОтменитьВыполнениеВсехПотоков(Группы);
			
			Пока ОбработчикиВыполнялись Цикл
				Поток = ДобавитьПотокОбработчикаОтложенногоОбновления(СведенияОбОбновлении); // @skip-check query-in-loop - многопоточное обновление.
				
				ОчередиДляОчистки = ОчередиДляОчистки(Обработанные); // @skip-check query-in-loop - получение актуальных данных об обработанных очередях.
				ОчиститьОбработанныеОчереди(ОчередиДляОчистки, Обработанные, СведенияОбОбновлении);
				
				Если ТипЗнч(Поток) = Тип("СтрокаТаблицыЗначений") Тогда
					ВыполнитьПоток(Группы, Поток);
					ОжидатьСвободныйПоток(Группы); // @skip-check query-in-loop - многопоточное обновление.
				ИначеЕсли Поток = Истина Тогда
					ОжидатьЗавершениеЛюбогоПотока(Группы); // @skip-check query-in-loop - многопоточное обновление.
				ИначеЕсли Поток = Ложь Тогда
					ОбработчикиВыполнялись = Ложь;
					ОжидатьЗавершениеВсехПотоков(Группы); // @skip-check query-in-loop - многопоточное обновление.
					Прервать;
				ИначеЕсли Поток = "ПрерватьВыполнение" Тогда
					ОжидатьЗавершениеВсехПотоков(Группы); // @skip-check query-in-loop - многопоточное обновление.
					Прервать;
				КонецЕсли;
				
				Если ДатаПоследнейПроверки = Неопределено
					Или ТекущаяДатаСеанса() - ДатаПоследнейПроверки > 600 Тогда
					ДатаПоследнейПроверки = ТекущаяДатаСеанса();
					ОчиститьПрогрессОбработкиЗаИнтервалыПредыдущегоДня(); // @skip-check query-in-loop - многопоточное обновление.
				КонецЕсли;
				
				Задание = РегламентныеЗаданияСервер.Задание(Метаданные.РегламентныеЗадания.ОтложенноеОбновлениеИБ);
				ТребуетсяВыполнение = Задание.Расписание.ТребуетсяВыполнение(ТекущаяДатаСеанса());
				
				Если Не ТребуетсяВыполнение Или Не ФорсироватьОбновление(СведенияОбОбновлении) Тогда
					ОжидатьЗавершениеВсехПотоков(Группы); // @skip-check query-in-loop - многопоточное обновление.
					УдалитьВсеПотокиОбновления();
					Прервать;
				КонецЕсли;
				
			КонецЦикла;
		Иначе
			ОчиститьПрогрессОбработкиЗаИнтервалыПредыдущегоДня();
			ОбработчикиВыполнялись = ВыполнитьОтложенныйОбработчикОбновления();
		КонецЕсли;
	Исключение
		ЗаписатьОшибку(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		ОтменитьВыполнениеВсехПотоков(Группы);
		УдалитьВсеПотокиОбновления();
	КонецПопытки;
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	Если Не ОбработчикиВыполнялись Или ВыполненыВсеОтложенныеОбработчики(СведенияОбОбновлении) Тогда
		УдалитьВсеПотокиОбновления();
		ОтключитьОтложенноеОбновление();
		ОчиститьТранзакцииЗапускаОбработчиков();
		ВыполнитьДействияПослеОтложенногоОбновленияИнформационнойБазы();
	КонецЕсли;
	
КонецПроцедуры

Процедура ОчиститьТранзакцииЗапускаОбработчиков()
	
	НаборЗаписей = РегистрыСведений.ФиксацияОбработкиДанныхОбработчиками.СоздатьНаборЗаписей();
	НаборЗаписей.Записать();
	
КонецПроцедуры

Процедура ОчиститьПрогрессОбработкиЗаИнтервалыПредыдущегоДня()
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИнтервалЧас", НачалоДня(ТекущаяДатаСеанса()));
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ПрогрессОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ПрогрессОбновления.ИнтервалЧас КАК ИнтервалЧас,
		|	ПрогрессОбновления.КлючЗаписи КАК КлючЗаписи
		|ИЗ
		|	РегистрСведений.ПрогрессОбновления КАК ПрогрессОбновления
		|ГДЕ
		|	ПрогрессОбновления.ИнтервалЧас < &ИнтервалЧас";
	Результат = Запрос.Выполнить().Выгрузить();
	
	Для Каждого Строка Из Результат Цикл
		МенеджерЗаписи = РегистрыСведений.ПрогрессОбновления.СоздатьМенеджерЗаписи();
		МенеджерЗаписи.ИмяОбработчика = Строка.ИмяОбработчика;
		МенеджерЗаписи.ИнтервалЧас = Строка.ИнтервалЧас;
		МенеджерЗаписи.КлючЗаписи = Строка.КлючЗаписи;
		МенеджерЗаписи.Удалить();
	КонецЦикла;
	
КонецПроцедуры

Функция ОбработчикиДляОтложеннойРегистрацииДанных(БезФильтра = Ложь, ПерезапускОбновления = Ложь, РегистрируемыеОбработчики = Неопределено) Экспорт
	
	Если РегистрируемыеОбработчики = Неопределено Тогда
		РегистрируемыеОбработчики = Новый Массив;
	КонецЕсли;
	
	ЭтоПодчиненныйУзелРИБСФильтром = ОбщегоНазначения.ЭтоПодчиненныйУзелРИБСФильтром();
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполненияОбработчика", Перечисления.РежимыВыполненияОтложенныхОбработчиков.Параллельно);
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.УстановитьПараметр("БезФильтра", БезФильтра);
	Запрос.УстановитьПараметр("ЭтоПодчиненныйУзелРИБСФильтром", ЭтоПодчиненныйУзелРИБСФильтром);
	// Отборы для перезапуска отложенного обновления.
	Запрос.УстановитьПараметр("СтатусОбработчика", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.УстановитьПараметр("РегистрируемыеОбработчики", РегистрируемыеОбработчики);
	Запрос.УстановитьПараметр("ОтключитьОтбор", (Не ПерезапускОбновления Или РегистрируемыеОбработчики.Количество() = 0));
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ОбработчикиОбновления.ИмяБиблиотеки КАК ИмяБиблиотеки,
		|	ОбработчикиОбновления.Версия КАК Версия,
		|	ОбработчикиОбновления.ПроцедураЗаполненияДанныхОбновления КАК ПроцедураЗаполненияДанныхОбновления,
		|	ОбработчикиОбновления.ОчередьОтложеннойОбработки КАК ОчередьОтложеннойОбработки,
		|	ОбработчикиОбновления.Многопоточный КАК Многопоточный,
		|	ОбработчикиОбновления.ОбрабатываемыеДанные КАК ОбрабатываемыеДанные,
		|	ОбработчикиОбновления.Статус КАК Статус
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|	И (ОбработчикиОбновления.РежимВыполненияОтложенногоОбработчика = &РежимВыполненияОбработчика
		|			ИЛИ &БезФильтра)
		|	И (ОбработчикиОбновления.Статус <> &СтатусОбработчика
		|			ИЛИ ОбработчикиОбновления.ИмяОбработчика В(&РегистрируемыеОбработчики)
		|			ИЛИ &ОтключитьОтбор)
		|	И (&ЭтоПодчиненныйУзелРИБСФильтром
		|		И ОбработчикиОбновления.ЗапускатьИВПодчиненномУзлеРИБСФильтрами
		|		ИЛИ НЕ &ЭтоПодчиненныйУзелРИБСФильтром)
		|
		|УПОРЯДОЧИТЬ ПО
		|	ОбработчикиОбновления.ОчередьОтложеннойОбработки";
	Обработчики = Запрос.Выполнить().Выгрузить();
	
	Возврат Обработчики;
	
КонецФункции

Функция НайтиОбработчикОбновления(КонтекстОбработчика, ПараметрыОбновления = Неопределено)
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	ТекущийПорядок       = ТекущийПорядокОбновления();
	ДлительностьЭтаповОбновления = СведенияОбОбновлении.ДлительностьЭтаповОбновления;
	ЗаписатьДлительностьЭтапов = Ложь;
	Если Не ЗначениеЗаполнено(ДлительностьЭтаповОбновления.Критичные.Начало) Тогда
		ДлительностьЭтаповОбновления.Критичные.Начало = ТекущаяДатаСеанса();
		ЗаписатьДлительностьЭтапов = Истина;
	ИначеЕсли ОбщегоНазначения.РазделениеВключено() Тогда
		Если ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Обычный
			И Не ЗначениеЗаполнено(ДлительностьЭтаповОбновления.Обычные.Начало) Тогда
			ДлительностьЭтаповОбновления.Обычные.Начало = ТекущаяДатаСеанса();
			ЗаписатьДлительностьЭтапов = Истина;
		ИначеЕсли ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Некритичный
			И Не ЗначениеЗаполнено(ДлительностьЭтаповОбновления.Некритичные.Начало) Тогда
			ДлительностьЭтаповОбновления.Некритичные.Начало = ТекущаяДатаСеанса();
			ЗаписатьДлительностьЭтапов = Истина;
		КонецЕсли;
	КонецЕсли;
	
	Если ЗаписатьДлительностьЭтапов Тогда
		ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
	КонецЕсли;
	
	// Получение групп, по которым еще не выполнены все обработчики обновления.
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ОбработчикиОбновления.ГруппаОбновления КАК ГруппаОбновления
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.Статус <> &Статус
		|	И ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения";
	ГруппыОбработчиковИЗависимость = Запрос.Выполнить().Выгрузить();
	
	// Выбор невыполненных обработчиков обновления.
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ОбработчикиОбновления.Статус КАК Статус,
		|	ОбработчикиОбновления.Версия КАК Версия,
		|	ОбработчикиОбновления.ИмяБиблиотеки КАК ИмяБиблиотеки,
		|	ОбработчикиОбновления.ДлительностьОбработки КАК ДлительностьОбработки,
		|	ОбработчикиОбновления.РежимВыполнения КАК РежимВыполнения,
		|	ОбработчикиОбновления.ВерсияРегистрации КАК ВерсияРегистрации,
		|	ОбработчикиОбновления.ВерсияПорядок КАК ВерсияПорядок,
		|	ОбработчикиОбновления.Идентификатор КАК Идентификатор,
		|	ОбработчикиОбновления.ЧислоПопыток КАК ЧислоПопыток,
		|	ОбработчикиОбновления.ИнформацияОбОшибке КАК ИнформацияОбОшибке,
		|	ОбработчикиОбновления.Комментарий КАК Комментарий,
		|	ОбработчикиОбновления.Приоритет КАК Приоритет,
		|	ОбработчикиОбновления.ПроцедураПроверки КАК ПроцедураПроверки,
		|	ОбработчикиОбновления.ПроцедураЗаполненияДанныхОбновления КАК ПроцедураЗаполненияДанныхОбновления,
		|	ОбработчикиОбновления.ОчередьОтложеннойОбработки КАК ОчередьОтложеннойОбработки,
		|	ОбработчикиОбновления.ЗапускатьТолькоВГлавномУзле КАК ЗапускатьТолькоВГлавномУзле,
		|	ОбработчикиОбновления.ЗапускатьИВПодчиненномУзлеРИБСФильтрами КАК ЗапускатьИВПодчиненномУзлеРИБСФильтрами,
		|	ОбработчикиОбновления.Многопоточный КАК Многопоточный,
		|	ОбработчикиОбновления.ОбработкаПорцииЗавершена КАК ОбработкаПорцииЗавершена,
		|	ОбработчикиОбновления.ГруппаОбновления КАК ГруппаОбновления,
		|	ОбработчикиОбновления.ИтерацияЗапуска КАК ИтерацияЗапуска,
		|	ОбработчикиОбновления.ОбрабатываемыеДанные КАК ОбрабатываемыеДанные,
		|	ОбработчикиОбновления.СтатистикаВыполнения КАК СтатистикаВыполнения,
		|	ОбработчикиОбновления.РежимВыполненияОтложенногоОбработчика КАК РежимВыполненияОтложенногоОбработчика,
		|	ОбработчикиОбновления.ИзменяемыеОбъекты КАК ИзменяемыеОбъекты,
		|	ОбработчикиОбновления.Порядок,
		|	ОбработчикиОбновления.ИспользуетсяРазделениеПоКритичности,
		|	ОбработчикиОбновления.ОбработаныАктуальныеДанные
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|	И ОбработчикиОбновления.Статус <> &Статус
		|
		|УПОРЯДОЧИТЬ ПО
		|	ГруппаОбновления,
		|	ОчередьОтложеннойОбработки";
	Обработчики = Запрос.Выполнить().Выгрузить();
	
	Пока Истина Цикл
		Результат = ОбработчикДляВыполнения(Обработчики, ГруппыОбработчиковИЗависимость, СведенияОбОбновлении);
		
		Если Результат.Свойство("ПерейтиКСледующемуПорядкуОбновления") Тогда
			
			Если ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Критичный Тогда
				ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Обычный;
			ИначеЕсли ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Обычный Тогда
				ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Некритичный;
			КонецЕсли;
			
			РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
			
			Если ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Обычный Тогда
				ДлительностьЭтаповОбновления.Критичные.Конец = ТекущаяДатаСеанса();
				Если Не РазделениеВключено Тогда
					ДлительностьЭтаповОбновления.Обычные.Начало  = ТекущаяДатаСеанса();
				КонецЕсли;
			Иначе
				ДлительностьЭтаповОбновления.Обычные.Конец = ТекущаяДатаСеанса();
				Если Не РазделениеВключено Тогда
					ДлительностьЭтаповОбновления.Некритичные.Начало = ТекущаяДатаСеанса();
				КонецЕсли;
			КонецЕсли;
			ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
			
			Если РазделениеВключено Тогда
				Возврат "ПрерватьВыполнение";
			Иначе
				Константы.ПорядокОбрабатываемыхДанных.Установить(ТекущийПорядок);
			КонецЕсли;
			
			Продолжить;
		КонецЕсли;
		
		Если Результат.ОбработчикДляВыполнения = Неопределено
			И Результат.ЕстьНевыполненные Тогда
			СведенияОбОбновлении.ТекущаяИтерацияОбновления = СведенияОбОбновлении.ТекущаяИтерацияОбновления + 1;
			ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
		Иначе
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	ОбработчикДляВыполнения = Результат.ОбработчикДляВыполнения;
	Если ОбработчикДляВыполнения = Неопределено Тогда
		Если Результат.ЕстьВыполняющиеся Тогда
			Возврат Истина;
		Иначе
			СведенияОбОбновлении.ВремяОкончаниеОтложенногоОбновления = ТекущаяДатаСеанса();
			СведенияОбОбновлении.ОтложенноеОбновлениеЗавершеноУспешно = Результат.ЗавершеноУспешно;
			ДлительностьЭтаповОбновления.Некритичные.Конец = ТекущаяДатаСеанса();
			ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
			Константы.ОтложенноеОбновлениеЗавершеноУспешно.Установить(Результат.ЗавершеноУспешно);
			Если Не ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ() Тогда
				Константы.ОтложенноеОбновлениеВГлавномУзлеЗавершеноУспешно.Установить(Результат.ЗавершеноУспешно);
			КонецЕсли;
			
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	ПараллельныйРежим = (ОбработчикДляВыполнения.РежимВыполненияОтложенногоОбработчика = Перечисления.РежимыВыполненияОтложенныхОбработчиков.Параллельно);
	ПараметрыОбновления = ?(ПараметрыОбновления = Неопределено, Новый Структура, ПараметрыОбновления);
	ПараметрыОбновления.Вставить("ПараллельныйРежим", ПараллельныйРежим);
	Если ПараллельныйРежим Тогда
		ПараметрыОбновления.Вставить("ОчередьОбработчиков", ОчередьОбработчиковГруппыОбновления(ОбработчикДляВыполнения.ГруппаОбновления));
		ПараметрыОбновления.Вставить("ЕстьОбработчикиГлавногоУзла", ЕстьОбработчикиТолькоГлавногоУзла());
	КонецЕсли;
	
	УстановитьПараметрыОбработчикаОбновления(ОбработчикДляВыполнения, Истина, ПараллельныйРежим);
	ПередЗапускомПроцедурыОбработкиДанных(КонтекстОбработчика,
		ОбработчикДляВыполнения,
		ПараметрыОбновления,
		СведенияОбОбновлении);
	
	КонтекстОбработчика.ИдентификаторОбработчика = ОбработчикДляВыполнения.Идентификатор;
	КонтекстОбработчика.ИмяОбработчика = ОбработчикДляВыполнения.ИмяОбработчика;
	КонтекстОбработчика.ПараллельныйРежим = ПараллельныйРежим;
	КонтекстОбработчика.ПараметрыОбновления = ПараметрыОбновления;
	КонтекстОбработчика.ПараметрыОбработчикаОбновления = ПараметрыСеанса.ПараметрыОбработчикаОбновления;
	КонтекстОбработчика.ТекущаяИтерацияОбновления = СведенияОбОбновлении.ТекущаяИтерацияОбновления;
	
	УстановитьПараметрыОбработчикаОбновления(Неопределено);
	
	Возврат ОбработчикДляВыполнения;
	
КонецФункции

Процедура СброситьСведенияОбОбновлении(СведенияОбОбновлении)
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	НовыеСведенияОбОбновлении = НовыеСведенияОбОбновлении();
	
	Константы.ОтложенноеОбновлениеЗавершеноУспешно.Установить(Ложь);
	
	СведенияОбОбновлении.Вставить("ВремяНачалаОбновления");
	СведенияОбОбновлении.Вставить("ВремяОкончанияОбновления");
	СведенияОбОбновлении.Вставить("ПродолжительностьОбновления");
	СведенияОбОбновлении.Вставить("ВремяНачалаОтложенногоОбновления");
	СведенияОбОбновлении.Вставить("ВремяОкончаниеОтложенногоОбновления");
	СведенияОбОбновлении.Вставить("НомерСеанса", Новый СписокЗначений());
	СведенияОбОбновлении.Вставить("ОтложенноеОбновлениеЗавершеноУспешно");
	СведенияОбОбновлении.Вставить("ВыводитьОписаниеОбновлений", Ложь);
	СведенияОбОбновлении.Вставить("ПриостановленныеПроцедурыОбновления", Новый Массив);
	СведенияОбОбновлении.Вставить("ЗапущенныеПроцедурыОбновления", Новый Массив);
	Если СведенияОбОбновлении.УправлениеОтложеннымОбновлением.Свойство("ФорсироватьОбновление") Тогда
		СведенияОбОбновлении.Вставить("УправлениеОтложеннымОбновлением", Новый Структура("ФорсироватьОбновление"));
	Иначе
		СведенияОбОбновлении.Вставить("УправлениеОтложеннымОбновлением", Новый Структура);
	КонецЕсли;
	СведенияОбОбновлении.Вставить("ТекущаяИтерацияОбновления", 1);
	СведенияОбОбновлении.Вставить("ЗависимостьГруппОбработчиков", Новый Соответствие);
	СведенияОбОбновлении.Вставить("ЧитаемыеИИзменяемыеТаблицы", Новый Соответствие);
	СведенияОбОбновлении.ВерсияДереваОбработчиков = Метаданные.Версия;
	
	СведенияОбОбновлении.Вставить("ДлительностьЭтаповОбновления", НовыеСведенияОбОбновлении.ДлительностьЭтаповОбновления);
	
	ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
	
	ЗаписатьСведенияОБлокируемыхОбъектах(НовыеСведенияОБлокируемыхОбъектах());
КонецПроцедуры

Процедура ПроверитьСвойстваОтложенногоОбработчика(Знач Обработчик, Знач РежимВыполненияОтложенныхОбработчиков, ТекстОшибок)
	
	Если РежимВыполненияОтложенныхОбработчиков = "Параллельно"
		И Не ЗначениеЗаполнено(Обработчик.ПроцедураЗаполненияДанныхОбновления) Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не указана процедура заполнения данных
					   |отложенного обработчика обновления
					   |""%1"".'"),
			Обработчик.ИмяОбработчика);
		
		ЗаписатьОшибку(ТекстОшибки);
		ТекстОшибок = ТекстОшибок + ТекстОшибки + Символы.ПС;
	КонецЕсли;

	Если Обработчик.МонопольныйРежим = Истина Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'У отложенного обработчика ""%1""
			|не должен быть установлен признак ""%2"".'"), 
			Обработчик.ИмяОбработчика,
			"МонопольныйРежим");
		ЗаписатьОшибку(ТекстОшибки);
		ТекстОшибок = ТекстОшибок + ТекстОшибки + Символы.ПС;
	КонецЕсли;

	Если РежимВыполненияОтложенныхОбработчиков = "Параллельно" И Обработчик.ЗапускатьТолькоВГлавномУзле
		И Обработчик.ЗапускатьИВПодчиненномУзлеРИБСФильтрами Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'У отложенного обработчика ""%1""
			|некорректно заполнены значения свойств:
			| - ""%2""
			| - ""%3"".
			|
			|Данные свойства не могут одновременно принимать значение ""Истина"".'"), 
			Обработчик.ИмяОбработчика,
			"ЗапускатьТолькоВГлавномУзле",
			"ЗапускатьИВПодчиненномУзлеРИБСФильтрами");
		ЗаписатьОшибку(ТекстОшибки);
		ТекстОшибок = ТекстОшибок + ТекстОшибки + Символы.ПС;
	КонецЕсли;

	Если Обработчик.ОбщиеДанные = Истина Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'У отложенного обработчика ""%1""
			|указано недопустимое значение свойства ""%2"".
			|
			|Данное свойство не может принимать значение ""Истина"" у отложенного обработчика.'"), 
			Обработчик.ИмяОбработчика, "ОбщиеДанные");
		ЗаписатьОшибку(ТекстОшибки);
		ТекстОшибок = ТекстОшибок + ТекстОшибки + Символы.ПС;
	КонецЕсли;
	
КонецПроцедуры

Процедура ПроверитьУникальностьИдентификаторовОтложенныхОбработчиков(ИтерацииОбновления)
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	ТаблицаПроверкиУникальности = Новый ТаблицаЗначений;
	ТаблицаПроверкиУникальности.Колонки.Добавить("Идентификатор");
	ТаблицаПроверкиУникальности.Колонки.Добавить("Индекс");
	
	Для Каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
		
		ПараметрыОтбора = Новый Структура;
		ПараметрыОтбора.Вставить("РежимВыполнения", "Отложенно");
		ТаблицаОбработчиков = ИтерацияОбновления.Обработчики;
		
		Обработчики = ТаблицаОбработчиков.НайтиСтроки(ПараметрыОтбора);
		Для Каждого Обработчик Из Обработчики Цикл
			Если Не ЗначениеЗаполнено(Обработчик.Идентификатор) Тогда
				Продолжить;
			КонецЕсли;
			СтрокаТаблицы = ТаблицаПроверкиУникальности.Добавить();
			СтрокаТаблицы.Идентификатор = Строка(Обработчик.Идентификатор);
			СтрокаТаблицы.Индекс        = 1;
		КонецЦикла;
		
	КонецЦикла;
	
	ИсходноеКоличествоСтрок = ТаблицаПроверкиУникальности.Количество();
	ТаблицаПроверкиУникальности.Свернуть("Идентификатор", "Индекс");
	ИтоговоеКоличествоСтрок = ТаблицаПроверкиУникальности.Количество();
	
	// Быстрая проверка.
	Если ИсходноеКоличествоСтрок = ИтоговоеКоличествоСтрок Тогда
		Возврат; // Все идентификаторы уникальны.
	КонецЕсли;
	
	ТаблицаПроверкиУникальности.Сортировать("Индекс Убыв");
	ТекстСообщения = НСтр("ru = 'Обнаружены отложенные обработчики обновления,
		|у которых совпадают уникальные идентификаторы. Следующие идентификаторы не уникальны:'");
	Для Каждого СтрокаИдентификатор Из ТаблицаПроверкиУникальности Цикл
		Если СтрокаИдентификатор.Индекс = 1 Тогда
			Прервать;
		Иначе
			ТекстСообщения = ТекстСообщения + Символы.ПС + СтрокаИдентификатор.Идентификатор;
		КонецЕсли;
	КонецЦикла;
	
	ВызватьИсключение ТекстСообщения;
	
КонецПроцедуры

Процедура ДобавитьОтложенныеОбработчики(ИмяБиблиотеки, ОбработчикиПоВерсиям, ГруппаОбновления, ТекстОшибок, ТекущийРежимВыполнения = "")
	
	ЭтоПодчиненныйУзелРИБ = ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ();
	ЭтоПодчиненныйУзелРИБСФильтром = ОбщегоНазначения.ЭтоПодчиненныйУзелРИБСФильтром();
	ОписанияПодсистем = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем();
	
	ОписаниеПодсистемы = ОписанияПодсистем.ПоИменам[ИмяБиблиотеки];
	ПараллельноСВерсии = ОписаниеПодсистемы.ПараллельноеОтложенноеОбновлениеСВерсии;
	РежимВыполненияОтложенныхОбработчиков = ОписаниеПодсистемы.РежимВыполненияОтложенныхОбработчиков;
	
	НаборРазделенныхОбработчиков   = РегистрыСведений.ОбработчикиОбновления.СоздатьНаборЗаписей();
	НаборРазделенныхОбработчиков.Прочитать();
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	ЗависимостьГруппОбработчиков = СведенияОбОбновлении.ЗависимостьГруппОбработчиков;
	ЧитаемыеИИзменяемыеТаблицы   = СведенияОбОбновлении.ЧитаемыеИИзменяемыеТаблицы;
	
	ПропуститьПроверку     = Ложь;
	СоздаватьНовуюИтерацию = Истина;
	Итерация = 1;
	ЕстьОбработчикиТолькоГлавногоУзла = Ложь;
	СведенияОБлокируемыхОбъектах = СведенияОБлокируемыхОбъектах();
	
	ОтборКритичных = Новый Структура("Порядок", Перечисления.ПорядокОбработчиковОбновления.Критичный);
	ЕстьКритичныеОбработчики = (ОбработчикиПоВерсиям.НайтиСтроки(ОтборКритичных, Истина).Количество() > 0);
	
	Для Каждого СтрокаВерсия Из ОбработчикиПоВерсиям Цикл
		ЗаполнитьБлокируемыеОбъекты(СтрокаВерсия, СведенияОБлокируемыхОбъектах);
		
		Если РежимВыполненияОтложенныхОбработчиков = "Последовательно" Тогда
			Если ТекущийРежимВыполнения = "Параллельно" Тогда
				// У предыдущей библиотеки был параллельный режим, поэтому
				// для текущей создаем отдельную группу обновления.
				ГруппаОбновления = ГруппаОбновления + 1;
			КонецЕсли;
			ЗависимостьГруппОбработчиков.Вставить(ГруппаОбновления, Итерация <> 1);
			ТекущийРежимВыполнения = "Последовательно";
		ИначеЕсли РежимВыполненияОтложенныхОбработчиков = "Параллельно" И Не ЗначениеЗаполнено(ПараллельноСВерсии) Тогда
			ЗависимостьГруппОбработчиков.Вставить(ГруппаОбновления, Ложь);
			ТекущийРежимВыполнения = "Параллельно";
			СоздаватьНовуюИтерацию = Ложь;
		ИначеЕсли РежимВыполненияОтложенныхОбработчиков = "Параллельно" И ЗначениеЗаполнено(ПараллельноСВерсии) И Не ПропуститьПроверку Тогда
			НомерВерсии = СтрокаВерсия.Версия;
			Если НомерВерсии = "*" Тогда
				Результат = -1;
			ИначеЕсли СтрНачинаетсяС(НомерВерсии, "ОтладкаОбработчика") Тогда
				Результат = 1;
			Иначе
				Результат = ОбщегоНазначенияКлиентСервер.СравнитьВерсии(НомерВерсии, ПараллельноСВерсии);
			КонецЕсли;
			
			Если Результат < 0 Тогда
				Если ТекущийРежимВыполнения = "Параллельно" Тогда
					// У предыдущей библиотеки был параллельный режим, поэтому
					// для текущей создаем отдельную группу обновления.
					ГруппаОбновления = ГруппаОбновления + 1;
				КонецЕсли;
				ЗависимостьГруппОбработчиков.Вставить(ГруппаОбновления, Итерация <> 1);
				ТекущийРежимВыполнения = "Последовательно";
			Иначе
				ЗависимостьГруппОбработчиков.Вставить(ГруппаОбновления, Итерация <> 1);
				ПропуститьПроверку = Истина;
				ТекущийРежимВыполнения = "Параллельно";
				СоздаватьНовуюИтерацию = Ложь;
			КонецЕсли;
		КонецЕсли;
		
		Для Каждого Обработчик Из СтрокаВерсия.Строки Цикл
			ПроверитьСвойстваОтложенногоОбработчика(Обработчик, ТекущийРежимВыполнения, ТекстОшибок);
			Если ТекущийРежимВыполнения = "Параллельно" Тогда
				ЗаполнитьЧитаемыеИИзменяемыеДанные(Обработчик, ЧитаемыеИИзменяемыеТаблицы);
			КонецЕсли;
			
			Запись = НаборРазделенныхОбработчиков.Добавить();
			
			ЗаполнитьЗначенияСвойств(Запись, Обработчик, , "РежимВыполнения,ВерсияПорядок");
			РежимВыполнения = Перечисления.РежимыВыполненияОбработчиков[Обработчик.РежимВыполнения];
			
			Если Не ЗначениеЗаполнено(Запись.Порядок) Тогда
				Запись.Порядок = Перечисления.ПорядокОбработчиковОбновления.Обычный;
			КонецЕсли;
			Запись.ВерсияПорядок = СтрокаВерсия.ВерсияПорядок;
			Запись.РежимВыполнения = РежимВыполнения;
			Запись.Статус = Перечисления.СтатусыОбработчиковОбновления.НеВыполнялся;
			Запись.ИмяБиблиотеки = ИмяБиблиотеки;
			Запись.ГруппаОбновления = ГруппаОбновления;
			Запись.СтатистикаВыполнения = Новый ХранилищеЗначения(Новый Соответствие);
			Если ТекущийРежимВыполнения = "Параллельно" Тогда
				Запись.РежимВыполненияОтложенногоОбработчика = Перечисления.РежимыВыполненияОтложенныхОбработчиков.Параллельно;
			Иначе
				Запись.РежимВыполненияОтложенногоОбработчика = Перечисления.РежимыВыполненияОтложенныхОбработчиков.Последовательно;
				Если ЗначениеЗаполнено(ПараллельноСВерсии) И ЕстьКритичныеОбработчики Тогда
					Запись.Порядок = Перечисления.ПорядокОбработчиковОбновления.Критичный;
				КонецЕсли;
			КонецЕсли;
			
			// Определение значения константы ОтложенноеОбновлениеВГлавномУзлеЗавершеноУспешно.
			Если ТекущийРежимВыполнения = "Параллельно" И Не ЭтоПодчиненныйУзелРИБ
				И Обработчик.ЗапускатьТолькоВГлавномУзле = Истина Тогда
				ЕстьОбработчикиТолькоГлавногоУзла = Истина;
			КонецЕсли;
			Если ТекущийРежимВыполнения = "Параллельно" И ЭтоПодчиненныйУзелРИБСФильтром
				И Не Обработчик.ЗапускатьИВПодчиненномУзлеРИБСФильтрами Тогда
				ЕстьОбработчикиТолькоГлавногоУзла = Истина;
				// В подчиненном узле РИБ обработчики главного узла не выполняются.
				Запись.Статус = Перечисления.СтатусыОбработчиковОбновления.Выполнен;
			КонецЕсли;
			
		КонецЦикла;
		
		Если СоздаватьНовуюИтерацию Тогда
			ГруппаОбновления = ГруппаОбновления + 1;
		КонецЕсли;
		Итерация = Итерация + 1;
	КонецЦикла;
	
	ЗаписатьСведенияОБлокируемыхОбъектах(СведенияОБлокируемыхОбъектах);
	ТекущееЗначение = Константы.ОтложенноеОбновлениеВГлавномУзлеЗавершеноУспешно.Получить();
	Константы.ОтложенноеОбновлениеВГлавномУзлеЗавершеноУспешно.Установить(ТекущееЗначение И Не ЕстьОбработчикиТолькоГлавногоУзла);
	
	// АПК:1327-выкл нет конкурентной работы с регистром
	НаборРазделенныхОбработчиков.Записать();
	// АПК:1327-вкл
	ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
	
КонецПроцедуры

Функция НезавершенныеОтложенныеОбработчики(СведенияОбОбновлении)
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ОбработчикиОбновления.Статус КАК Статус,
		|	ОбработчикиОбновления.Версия КАК Версия,
		|	ОбработчикиОбновления.ИмяБиблиотеки КАК ИмяБиблиотеки,
		|	ОбработчикиОбновления.ДлительностьОбработки КАК ДлительностьОбработки,
		|	ОбработчикиОбновления.РежимВыполнения КАК РежимВыполнения,
		|	ОбработчикиОбновления.ВерсияРегистрации КАК ВерсияРегистрации,
		|	ОбработчикиОбновления.Идентификатор КАК Идентификатор,
		|	ОбработчикиОбновления.ЧислоПопыток КАК ЧислоПопыток,
		|	ОбработчикиОбновления.СтатистикаВыполнения КАК СтатистикаВыполнения,
		|	ОбработчикиОбновления.ИнформацияОбОшибке КАК ИнформацияОбОшибке,
		|	ОбработчикиОбновления.Комментарий КАК Комментарий,
		|	ОбработчикиОбновления.Приоритет КАК Приоритет,
		|	ОбработчикиОбновления.ПроцедураПроверки КАК ПроцедураПроверки,
		|	ОбработчикиОбновления.ПроцедураЗаполненияДанныхОбновления КАК ПроцедураЗаполненияДанныхОбновления,
		|	ОбработчикиОбновления.ОчередьОтложеннойОбработки КАК ОчередьОтложеннойОбработки,
		|	ОбработчикиОбновления.ЗапускатьТолькоВГлавномУзле КАК ЗапускатьТолькоВГлавномУзле,
		|	ОбработчикиОбновления.ЗапускатьИВПодчиненномУзлеРИБСФильтрами КАК ЗапускатьИВПодчиненномУзлеРИБСФильтрами,
		|	ОбработчикиОбновления.Многопоточный КАК Многопоточный,
		|	ОбработчикиОбновления.ОбработкаПорцииЗавершена КАК ОбработкаПорцииЗавершена,
		|	ОбработчикиОбновления.ГруппаОбновления КАК ГруппаОбновления,
		|	ОбработчикиОбновления.ИтерацияЗапуска КАК ИтерацияЗапуска,
		|	ОбработчикиОбновления.ОбрабатываемыеДанные КАК ОбрабатываемыеДанные
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|	И ОбработчикиОбновления.Статус <> &Статус";
	Обработчики = Запрос.Выполнить().Выгрузить();
	
	ДеревоОбработчиков = СведенияОбОбновлении.ДеревоОбработчиков.Строки;
	Если ДеревоОбработчиков.Количество() > 0 Тогда
		СведенияОбОбновлении.ДеревоОбработчиков.Колонки.НомерВерсии.Имя = "Версия";
		Отбор = Новый Структура;
		Отбор.Вставить("Статус", "Выполняется");
		Выполняются = ДеревоОбработчиков.НайтиСтроки(Отбор, Истина);
		ПеренестиОбработчикиИзКонстанты(Обработчики, Выполняются);
		
		Отбор.Статус = "НеВыполнено";
		НеВыполнены = ДеревоОбработчиков.НайтиСтроки(Отбор, Истина);
		ПеренестиОбработчикиИзКонстанты(Обработчики, НеВыполнены);
		
		Отбор.Статус = "Ошибка";
		ЗавершилисьСОшибкой = ДеревоОбработчиков.НайтиСтроки(Отбор, Истина);
		ПеренестиОбработчикиИзКонстанты(Обработчики, ЗавершилисьСОшибкой);
		
		СведенияОбОбновлении.ДеревоОбработчиков = Новый ДеревоЗначений;
	КонецЕсли;
	
	Возврат Обработчики
	
КонецФункции

Процедура ДобавитьНезавершенныеОтложенныеОбработчики(ИтерацияОбновления, ИмяБиблиотеки,
		НезавершенныеОтложенныеОбработчики, Обработчики)
	
	Отбор = Новый Структура;
	Отбор.Вставить("ИмяБиблиотеки", ИмяБиблиотеки);
	НезавершенныеОбработчикиБиблиотеки = НезавершенныеОтложенныеОбработчики.НайтиСтроки(Отбор);
	Для Каждого Обработчик Из НезавершенныеОбработчикиБиблиотеки Цикл
		НайденныйОбработчик = Обработчики.Найти(Обработчик.ИмяОбработчика, "ИмяОбработчика", Истина);
		Если НайденныйОбработчик <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		Если ЗначениеЗаполнено(Обработчик.Идентификатор) Тогда
			НайденныйОбработчик = Обработчики.Найти(Обработчик.Идентификатор, "Идентификатор", Истина);
			Если НайденныйОбработчик <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		
		// Проверка, что обработчик не был удален. 
		ДополнительныйОтбор = Новый Структура;
		ДополнительныйОтбор.Вставить("РежимВыполнения", "Отложенно");
		ДополнительныйОтбор.Вставить("Процедура", Обработчик.ИмяОбработчика);
		НайденныйОбработчик = ИтерацияОбновления.Обработчики.НайтиСтроки(ДополнительныйОтбор);
		Если НайденныйОбработчик.Количество() = 0 Тогда
			Продолжить;
		Иначе
			НайденныйОбработчик = НайденныйОбработчик[0];
		КонецЕсли;
		
		// Необходимо запланировать выполнение незавершенного обработчика.
		СтрокаВерсия = Обработчики.Найти(НайденныйОбработчик.Версия, "Версия");
		Если СтрокаВерсия = Неопределено Тогда
			СтрокаВерсия = Обработчики.Добавить();
			СтрокаВерсия.Версия = НайденныйОбработчик.Версия;
		КонецЕсли;
		
		НовыйОбработчик = СтрокаВерсия.Строки.Добавить();
		ЗаполнитьЗначенияСвойств(НовыйОбработчик, НайденныйОбработчик, , "РежимВыполнения");
		НовыйОбработчик.ИмяОбработчика = НайденныйОбработчик.Процедура;
		НовыйОбработчик.РежимВыполнения = "Отложенно";
	КонецЦикла;
	
КонецПроцедуры

Процедура ПеренестиОбработчикиИзКонстанты(Приемник, Источник)
	
	Для Каждого Обработчик Из Источник Цикл
		НовыйОбработчик = Приемник.Добавить();
		ЗаполнитьЗначенияСвойств(НовыйОбработчик, Обработчик);
		НовыйОбработчик.РежимВыполнения = Перечисления.РежимыВыполненияОбработчиков.Отложенно;
	КонецЦикла;
	
КонецПроцедуры

Функция ОчередьОбработчиковГруппыОбновления(ГруппаОбновления)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ГруппаОбновления", ГруппаОбновления);
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ОчередьОтложеннойОбработки КАК Очередь,
		|	ОбработчикиОбновления.Идентификатор КАК Идентификатор,
		|	ОбработчикиОбновления.ИмяОбработчика КАК Обработчик
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.ГруппаОбновления = &ГруппаОбновления";
	Возврат Запрос.Выполнить().Выгрузить();
	
КонецФункции

Функция ЕстьОбработчикиТолькоГлавногоУзла()
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.ЗапускатьТолькоВГлавномУзле = ИСТИНА";
	Возврат Запрос.Выполнить().Выгрузить().Количество() > 0;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//     * ОбработчикДляВыполнения - Неопределено
//                               - СтрокаТаблицыЗначений:
//          ** ИмяОбработчика - Строка.
//          ** Идентификатор - УникальныйИдентификатор.
//     * ЗавершеноУспешно - Булево.
//     * ЕстьВыполняющиеся - Булево.
//     * ЕстьНевыполненные - Булево.
//
Функция ОбработчикДляВыполнения(Обработчики, ГруппыОбработчиковИЗависимость, СведенияОбОбновлении)
	
	ЗависимостьГруппОбработчиков = СведенияОбОбновлении.ЗависимостьГруппОбработчиков;
	ТекущаяИтерация              = СведенияОбОбновлении.ТекущаяИтерацияОбновления;
	ТекущийПорядок               = ТекущийПорядокОбновления();
	
	Выполняемые = Новый Соответствие;
	Выполняемые.Вставить(Перечисления.ПорядокОбработчиковОбновления.Критичный, Истина);
	Если ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Обычный Тогда
		Выполняемые.Вставить(Перечисления.ПорядокОбработчиковОбновления.Обычный, Истина);
	ИначеЕсли ТекущийПорядок = Перечисления.ПорядокОбработчиковОбновления.Некритичный Тогда
		Выполняемые.Вставить(Перечисления.ПорядокОбработчиковОбновления.Обычный, Истина);
		Выполняемые.Вставить(Перечисления.ПорядокОбработчиковОбновления.Некритичный, Истина);
	КонецЕсли;
	
	ТекущаяГруппаОбновления = Неопределено;
	ОбработчикДляВыполнения = Неопределено;
	ПропускаемыеГруппы      = Новый Массив;
	ЕстьВыполняющиеся       = Ложь;
	ЕстьОшибки              = Ложь;
	ЕстьНевыполненные       = Ложь;
	ВыполняющийсяМногопоточныйОбработчик = Неопределено;
	Для Каждого Обработчик Из Обработчики Цикл
		Если Обработчик.ЧислоПопыток >= МаксимумПопытокОбновления(Обработчик) Тогда
			ЕстьОшибки = Истина;
			Продолжить;
		КонецЕсли;
		
		// Фильтрация обработчиков не подходящих под текущий порядок выполнения.
		Если Выполняемые[Обработчик.Порядок] = Неопределено Тогда
			Продолжить;
		ИначеЕсли ТекущийПорядок <> Перечисления.ПорядокОбработчиковОбновления.Некритичный
			И Обработчик.ИспользуетсяРазделениеПоКритичности
			И Обработчик.ОбработаныАктуальныеДанные Тогда
			Продолжить;
		КонецЕсли;
		
		Если Обработчик.ИтерацияЗапуска = ТекущаяИтерация Тогда
			// Обработчик в этой итерации обновления уже запускался.
			ЕстьНевыполненные = Истина;
			Продолжить;
		КонецЕсли;
		
		Если ПропускаемыеГруппы.Найти(Обработчик.ГруппаОбновления) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		Если Обработчик.ГруппаОбновления <> 1
			И ЗависимостьГруппОбработчиков[Обработчик.ГруппаОбновления] = Истина Тогда
			НайденнаяСтрока = ГруппыОбработчиковИЗависимость.Найти(Обработчик.ГруппаОбновления - 1, "ГруппаОбновления");
			Если НайденнаяСтрока <> Неопределено Тогда
				ПропускаемыеГруппы.Добавить(Обработчик.ГруппаОбновления);
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		
		Если ТекущаяГруппаОбновления = Неопределено Тогда
			ТекущаяГруппаОбновления = Обработчик.ГруппаОбновления;
		ИначеЕсли ТекущаяГруппаОбновления <> Обработчик.ГруппаОбновления Тогда
			// При переходе к следующей группе обновления делается проверка ошибок по предыдущей группе
			// и наличия многопоточных обработчиков, для которых есть порция обновления.
			Если ВыполняющийсяМногопоточныйОбработчик <> Неопределено Тогда
				ОбработчикДляВыполнения = ВыполняющийсяМногопоточныйОбработчик;
				Прервать;
			КонецЕсли;
			ТекущаяГруппаОбновления = Обработчик.ГруппаОбновления;
		КонецЕсли;
		
		Если Обработчик.Статус = Перечисления.СтатусыОбработчиковОбновления.Выполняется
			И Не Обработчик.ОбработкаПорцииЗавершена Тогда
			Если Обработчик.Многопоточный Тогда
				Если ЕстьПорцииДляОбновления(Обработчик) Тогда
					// Есть дополнительные порции для обработки в новых потоках, будет взят в работу
					// при отсутствии других обработчиков, подходящих для выполнения.
					ВыполняющийсяМногопоточныйОбработчик = Обработчик;
				КонецЕсли;
			ИначеЕсли ПотокиОбновления().Количество() = 0 Тогда // @skip-check query-in-loop - получение актуального количества потоков.
				ОбработчикДляВыполнения = Обработчик;
			КонецЕсли;
			ЕстьВыполняющиеся = Истина;
			Продолжить;
		КонецЕсли;
		
		Если Обработчик.Статус = Перечисления.СтатусыОбработчиковОбновления.Приостановлен Тогда
			Продолжить;
		КонецЕсли;
		
		ОбработчикДляВыполнения = Обработчик;
		Прервать;
	КонецЦикла;
	
	Если ОбработчикДляВыполнения = Неопределено И ВыполняющийсяМногопоточныйОбработчик <> Неопределено Тогда
		ОбработчикДляВыполнения = ВыполняющийсяМногопоточныйОбработчик;
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("ОбработчикДляВыполнения", ОбработчикДляВыполнения);
	Результат.Вставить("ЗавершеноУспешно", Не ЕстьОшибки);
	Результат.Вставить("ЕстьВыполняющиеся", ЕстьВыполняющиеся);
	Результат.Вставить("ЕстьНевыполненные", ЕстьНевыполненные);
	
	Если ОбработчикДляВыполнения = Неопределено
		И Не ЕстьВыполняющиеся
		И Не ЕстьНевыполненные
		И ТекущийПорядок <> Перечисления.ПорядокОбработчиковОбновления.Некритичный Тогда
		Результат.Вставить("ПерейтиКСледующемуПорядкуОбновления");
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Функция ОбработчикОбновления(ИмяОбработчика)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИмяОбработчика", ИмяОбработчика);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ОбработчикиОбновления.Статус КАК Статус,
		|	ОбработчикиОбновления.Версия КАК Версия,
		|	ОбработчикиОбновления.ИмяБиблиотеки КАК ИмяБиблиотеки,
		|	ОбработчикиОбновления.ДлительностьОбработки КАК ДлительностьОбработки,
		|	ОбработчикиОбновления.РежимВыполнения КАК РежимВыполнения,
		|	ОбработчикиОбновления.ВерсияРегистрации КАК ВерсияРегистрации,
		|	ОбработчикиОбновления.ВерсияПорядок КАК ВерсияПорядок,
		|	ОбработчикиОбновления.Идентификатор КАК Идентификатор,
		|	ОбработчикиОбновления.ЧислоПопыток КАК ЧислоПопыток,
		|	ОбработчикиОбновления.СтатистикаВыполнения КАК СтатистикаВыполнения,
		|	ОбработчикиОбновления.ИнформацияОбОшибке КАК ИнформацияОбОшибке,
		|	ОбработчикиОбновления.Комментарий КАК Комментарий,
		|	ОбработчикиОбновления.Приоритет КАК Приоритет,
		|	ОбработчикиОбновления.ПроцедураПроверки КАК ПроцедураПроверки,
		|	ОбработчикиОбновления.ПроцедураЗаполненияДанныхОбновления КАК ПроцедураЗаполненияДанныхОбновления,
		|	ОбработчикиОбновления.ОчередьОтложеннойОбработки КАК ОчередьОтложеннойОбработки,
		|	ОбработчикиОбновления.ЗапускатьТолькоВГлавномУзле КАК ЗапускатьТолькоВГлавномУзле,
		|	ОбработчикиОбновления.ЗапускатьИВПодчиненномУзлеРИБСФильтрами КАК ЗапускатьИВПодчиненномУзлеРИБСФильтрами,
		|	ОбработчикиОбновления.Многопоточный КАК Многопоточный,
		|	ОбработчикиОбновления.ОбработкаПорцииЗавершена КАК ОбработкаПорцииЗавершена,
		|	ОбработчикиОбновления.ГруппаОбновления КАК ГруппаОбновления,
		|	ОбработчикиОбновления.ИтерацияЗапуска КАК ИтерацияЗапуска,
		|	ОбработчикиОбновления.ОбрабатываемыеДанные КАК ОбрабатываемыеДанные,
		|	ОбработчикиОбновления.РежимВыполненияОтложенногоОбработчика КАК РежимВыполненияОтложенногоОбработчика
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.ИмяОбработчика = &ИмяОбработчика";
	Результат = Запрос.Выполнить().Выгрузить();
	Если Результат.Количество() = 0 Тогда
		Возврат Неопределено;
	Иначе
		Возврат Результат[0];
	КонецЕсли;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Механизм многопоточного обновления.

// Имя группы потоков регистрации данных для отложенного обновления.
//
// Возвращаемое значение:
//  Строка - имя группы.
//
Функция ГруппаПотоковРегистрацииДанныхОтложенногоОбновления()
	
	Возврат "Регистрация";
	
КонецФункции

// Имя группы потоков отложенного обновления.
//
// Возвращаемое значение:
//  Строка - имя группы.
//
Функция ГруппаПотоковОтложенногоОбновления()
	
	Возврат "Обновление";
	
КонецФункции

// Имя группы потоков поиска порций данных для многопоточного выполнения обработчиков обновления.
//
// Возвращаемое значение:
//  Строка - имя группы.
//
Функция ГруппаПотоковПоискаПорций()
	
	Возврат "Поиск";
	
КонецФункции

// Создать новое описание потоков регистрации данных отложенного обновления.
//
// Возвращаемое значение:
//   см. НовоеОписаниеПотоков
//
Функция НовоеОписаниеГруппПотоковРегистрацииДанныхОтложенногоОбновления() Экспорт
	
	ГруппаРегистрации = НовоеОписаниеГруппыПотоков();
	ГруппаРегистрации.Процедура =
		"ОбновлениеИнформационнойБазыСлужебный.ЗаполнитьДанныеОтложенногоОбработчика";
	ГруппаРегистрации.ПроцедураЗавершения =
		"ОбновлениеИнформационнойБазыСлужебный.ЗавершитьРегистрациюДанныхОтложенногоОбновления";
	
	Группы = Новый Соответствие;
	Группы[ГруппаПотоковРегистрацииДанныхОтложенногоОбновления()] = ГруппаРегистрации;
	
	Возврат Группы;
	
КонецФункции

// Добавить поток регистрации данных отложенного обновления.
//
// Параметры:
//  ОписаниеОбрабатываемыхДанных - см. НовоеОписаниеОбрабатываемыхДанных
//
// Возвращаемое значение:
//  СтрокаТаблицыЗначений из см. НовоеОписаниеПотоков
//
Функция ДобавитьПотокРегистрацииДанныхОтложенногоОбновления(ОписаниеОбрабатываемыхДанных)
	
	ШаблонНаименования = НСтр("ru = 'Регистрация данных обработчика обновления ""%1""'");
	ОписаниеОбрабатываемыхДанных.Статус = "Выполняется";
	
	Поток = НовыйПоток();
	Поток.ПараметрыПроцедуры = ОписаниеОбрабатываемыхДанных;
	Поток.ПараметрыПроцедурыЗавершения = ОписаниеОбрабатываемыхДанных;
	Поток.Группа = ГруппаПотоковРегистрацииДанныхОтложенногоОбновления();
	Поток.Наименование = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонНаименования,
		ОписаниеОбрабатываемыхДанных.ИмяОбработчика);
	
	СохранитьПотокОбновления(Поток);
	
	Возврат Поток;
	
КонецФункции

// Завершить регистрацию данных отложенного обновления.
// Вызывается автоматически в основном потоке, после завершения выполнения ЗаполнитьДанныеОтложенногоОбработчика().
//
// Параметры:
//  ОписаниеОбрабатываемыхДанных - см. НовоеОписаниеОбрабатываемыхДанных
//  АдресРезультата - Строка - адрес временного хранилища с результатом из ЗаполнитьДанныеОтложенногоОбработчика().
//
Процедура ЗавершитьРегистрациюДанныхОтложенногоОбновления(ОписаниеОбрабатываемыхДанных,
                                                          АдресРезультата) Экспорт
	
	Результат = ПолучитьИзВременногоХранилища(АдресРезультата);
	
	ЗаполнитьЗначенияСвойств(ОписаниеОбрабатываемыхДанных, Результат,, "ЗарегистрированныеТаблицыРегистраторов");
	ОписаниеОбрабатываемыхДанных.Статус = "Выполнено";
	
	Если ЗначениеЗаполнено(Результат.ЗарегистрированныеТаблицыРегистраторов) Тогда
		ЗарегистрированныеТаблицы = Новый Массив;
		Для Каждого КлючИЗначение Из Результат.ЗарегистрированныеТаблицыРегистраторов Цикл
			ЗарегистрированныеТаблицы.Добавить(КлючИЗначение.Значение);
		КонецЦикла;
	КонецЕсли;
	ОписаниеОбрабатываемыхДанных.ЗарегистрированныеТаблицыРегистраторов = ЗарегистрированныеТаблицы;
	
	ДлительностьРегистрацииДанных = 0;
	Результат.Свойство("ДлительностьРегистрацииДанных", ДлительностьРегистрацииДанных);
	
	Если ЭтоОписаниеДанныхМногопоточногоОбработчика(ОписаниеОбрабатываемыхДанных) Тогда
		ИсправитьПолныеИменаВПараметрахВыборки(ОписаниеОбрабатываемыхДанных.ПараметрыВыборки);
	КонецЕсли;
	
	УказанФильтрАктуальныхДанных = УказанФильтрАктуальныхДанных(ОписаниеОбрабатываемыхДанных.АктуальныеДанные);
	ОбрабатываемыеДанные = Новый ХранилищеЗначения(ОписаниеОбрабатываемыхДанных, Новый СжатиеДанных(9));
	
	УстанавливаемыеСвойства = Новый Структура;
	УстанавливаемыеСвойства.Вставить("ДлительностьРегистрацииДанных", ДлительностьРегистрацииДанных);
	УстанавливаемыеСвойства.Вставить("ОбрабатываемыеДанные", ОбрабатываемыеДанные);
	Если УказанФильтрАктуальныхДанных Тогда
		УстанавливаемыеСвойства.Вставить("ИспользуетсяРазделениеПоКритичности", Истина);
	КонецЕсли;
	УстановитьСвойстваОбработчика(ОписаниеОбрабатываемыхДанных.ИмяОбработчика, УстанавливаемыеСвойства);
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		Если Результат.ДанныеОбновления <> Неопределено Тогда
			МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
			МодульОбменДаннымиСервер.СохранитьДанныеОбновления(Результат.ДанныеОбновления, Результат.ИмяФайлаСИзменениями);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Создать новое описание потоков обработчиков отложенного обновления.
//
// Возвращаемое значение:
//   см. НовоеОписаниеПотоков
//
Функция НовоеОписаниеГруппПотоковОбработчиковОтложенногоОбновления() Экспорт
	
	ГруппаОбновления = НовоеОписаниеГруппыПотоков();
	ГруппаОбновления.Процедура =
		"ОбновлениеИнформационнойБазыСлужебный.ВыполнитьОтложенныйОбработчик";
	ГруппаОбновления.ПроцедураЗавершения =
		"ОбновлениеИнформационнойБазыСлужебный.ЗавершитьВыполнениеОтложенногоОбработчика";
	ГруппаОбновления.ПриАварийномЗавершении =
		"ОбновлениеИнформационнойБазыСлужебный.ПриАварийномЗавершенииПотокаОтложенногоОбработчика";
	ГруппаОбновления.ПриОтменеПотока =
		"ОбновлениеИнформационнойБазыСлужебный.ПриОтменеПотокаОтложенногоОбработчика";
	
	ГруппаПоиска = НовоеОписаниеГруппыПотоков();
	ГруппаПоиска.Процедура =
		"ОбновлениеИнформационнойБазыСлужебный.НайтиПорциюДляОбновления";
	ГруппаПоиска.ПроцедураЗавершения =
		"ОбновлениеИнформационнойБазыСлужебный.ЗавершитьПоискПорцииДляОбновления";
	ГруппаПоиска.ПриАварийномЗавершении =
		"ОбновлениеИнформационнойБазыСлужебный.ПриАварийномЗавершенииПотокаПоискаПорцииДляОбновления";
	ГруппаПоиска.ПриОтменеПотока =
		"ОбновлениеИнформационнойБазыСлужебный.ПриОтменеПотокаПоискаПорцииДляОбновления";
	
	Группы = Новый Соответствие;
	Группы[ГруппаПотоковОтложенногоОбновления()] = ГруппаОбновления;
	Группы[ГруппаПотоковПоискаПорций()] = ГруппаПоиска;
	
	Возврат Группы;
	
КонецФункции

Функция НовыйПоток()
	
	Потоки = НовоеОписаниеПотоков();
	Поток = Потоки.Добавить();
	Поток.ИдентификаторПотока = Новый УникальныйИдентификатор;
	
	Возврат Поток;
	
КонецФункции

// Добавить поток обработчика отложенного обновления.
//
// Параметры:
//  ОписаниеПотоков - см. НовоеОписаниеПотоковОбработчиковОтложенногоОбновления
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//
// Возвращаемое значение:
//   СтрокаТаблицыЗначений из см. НовоеОписаниеПотоков
//
Функция ДобавитьПотокОбработчикаОтложенногоОбновления(СведенияОбОбновлении)
	
	Поток = Неопределено;
	
	Пока Поток = Неопределено Цикл
		КонтекстОбработчика = НовыйКонтекстОбработчика();
		ОбработчикОбновления = НайтиОбработчикОбновления(КонтекстОбработчика); // @skip-check query-in-loop - создание потока обновления.
		
		Если ТипЗнч(ОбработчикОбновления) = Тип("СтрокаТаблицыЗначений") Тогда
			Если КонтекстОбработчика.ВыполнитьОбработчик Тогда
				Поток = НовыйПоток();
				
				Если ОбработчикОбновления.Многопоточный Тогда
					ДополнитьКонтекстМногопоточногоОбработчика(КонтекстОбработчика);
					Добавлен = ДобавитьПотокПоискаДанныхДляОбновления(Поток,
						ОбработчикОбновления,
						КонтекстОбработчика,
						СведенияОбОбновлении);
					
					Если Не Добавлен Тогда
						Поток = Истина;
					КонецЕсли;
				Иначе
					ДобавитьПотокОбработчикаОбновления(Поток, КонтекстОбработчика);
				КонецЕсли;
			Иначе
				ЗавершитьВыполнениеОтложенногоОбработчика(КонтекстОбработчика, Неопределено); // @skip-check query-in-loop - создание потока обновления.
				Поток = Неопределено;
			КонецЕсли;
		Иначе
			Поток = ОбработчикОбновления;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Поток;
	
КонецФункции

// Добавить поток поиска данных для обработчика отложенного обновления.
//
// Параметры:
//  Поток - см. НовоеОписаниеПотоков
//  Обработчик - СтрокаДереваЗначений - обработчик обновления в виде строки дерева обработчиков.
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//
// Возвращаемое значение:
//  Булево - Истина, поток добавлен, иначе Ложь.
//
Функция ДобавитьПотокПоискаДанныхДляОбновления(Поток, Обработчик, КонтекстОбработчика, СведенияОбОбновлении)
	
	ИмяОбработчика = Обработчик.ИмяОбработчика;
	Описание = Обработчик.ОбрабатываемыеДанные.Получить();
	
	Если Не Описание.ВыполняетсяПоискПорции Тогда
		ПорцииДляОбновления = Описание.ПорцииДляОбновления;
		
		Если Описание.ПоискЗавершен И (ПорцииДляОбновления = Неопределено Или ПорцииДляОбновления.Количество() = 0) Тогда
			ПорцииДляОбновления = Неопределено;
			Описание.ПоследняяВыбраннаяЗапись = Неопределено;
			Описание.ПоискЗавершен = Ложь;
		КонецЕсли;
		
		ШаблонНаименования = НСтр("ru = 'Поиск данных для обработчика обновления ""%1""'");
		Поток.Наименование = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонНаименования, ИмяОбработчика);
		Поток.Группа = ГруппаПотоковПоискаПорций();
		Поток.ПриоритетЗавершения = 1;
		
		ПараметрыПоиска = НовыеПараметрыПоискаПорции();
		ПараметрыПоиска.ИмяОбработчика = ИмяОбработчика;
		ПараметрыПоиска.КонтекстОбработчика = КонтекстОбработчика;
		ПараметрыПоиска.ПараметрыВыборки = Описание.ПараметрыВыборки;
		ПараметрыПоиска.Очередь = КонтекстОбработчика.Параметры.Очередь;
		ПараметрыПоиска.ФорсироватьОбновление = ФорсироватьОбновление(СведенияОбОбновлении);
		
		НеобработаннаяПорция = ПерваяНеобработаннаяПорция(ПорцииДляОбновления);
		
		Если НеобработаннаяПорция <> Неопределено Тогда
			ПараметрыПоиска.ИдентификаторПорции = НеобработаннаяПорция.Идентификатор;
			ПараметрыПоиска.ПерваяЗапись = НеобработаннаяПорция.ПерваяЗапись;
			ПараметрыПоиска.ПоследняяЗапись = НеобработаннаяПорция.ПоследняяЗапись;
		ИначеЕсли Описание.ПоследняяВыбраннаяЗапись <> Неопределено Тогда
			ПараметрыПоиска.ПоследняяВыбраннаяЗапись = Описание.ПоследняяВыбраннаяЗапись;
		КонецЕсли;
		
		Поток.ПараметрыПроцедуры = ПараметрыПоиска;
		Поток.ПараметрыПроцедурыЗавершения = ПараметрыПоиска;
		Описание.ВыполняетсяПоискПорции = Истина;
		
		НачатьТранзакцию();
		Попытка
			УстановитьСвойствоОбработчика(ИмяОбработчика,
				"ОбрабатываемыеДанные",
				Новый ХранилищеЗначения(Описание));
			СохранитьПотокОбновления(Поток);
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
		Возврат Истина;
	Иначе
		Возврат Ложь;
	КонецЕсли;
	
КонецФункции

// Добавить поток обработчика отложенного обновления.
//
// Параметры:
//  Поток - см. НовоеОписаниеПотоков
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//
Процедура ДобавитьПотокОбработчикаОбновления(Поток, КонтекстОбработчика)
	
	ИмяОбработчика = КонтекстОбработчика.ИмяОбработчика;
	ШаблонНаименования = НСтр("ru = 'Выполнение обработчика обновления ""%1""'");
	Поток.Наименование = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонНаименования, ИмяОбработчика);
	Поток.Группа = ГруппаПотоковОтложенногоОбновления();
	Поток.ПараметрыПроцедуры = КонтекстОбработчика;
	Поток.ПараметрыПроцедурыЗавершения = КонтекстОбработчика;
	
	СохранитьПотокОбновления(Поток);
	
КонецПроцедуры

// Выполнить отложенный обработчик в фоновом задании.
// Выполняется только, если КонтекстОбработчика.ВыполнитьОбработчик = Истина (т.е. не в подчиненном узле РИБ).
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  АдресРезультата - Строка - адрес временного хранилища для сохранения результата выполнения процедуры.
//
Процедура ВыполнитьОтложенныйОбработчик(КонтекстОбработчика, АдресРезультата) Экспорт
	
	ПараметрыСеанса.ПараметрыОбработчикаОбновления = КонтекстОбработчика.ПараметрыОбработчикаОбновления;
	
	ПодсистемаСуществует = ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом");
	ОтключитьОбновлениеКлючейДоступа(Истина, ПодсистемаСуществует);
	Попытка
		ПараметрыВызова = Новый Массив;
		ПараметрыВызова.Добавить(КонтекстОбработчика.Параметры);
		Результат = НовыйРезультатВыполненияОтложенногоОбработчика();
		
		Если КонтекстОбработчика.Параметры.Свойство("ОбновляемыеДанные") Тогда
			ВсегоДанных = 0;
			Для Каждого СтрокаОбновляемыйОбъект Из КонтекстОбработчика.Параметры.ОбновляемыеДанные.НаборДанных Цикл
				ВсегоДанных = ВсегоДанных + СтрокаОбновляемыйОбъект.Данные.Количество();
			КонецЦикла;
			Результат.ПереданоДанныхДляОбработки = ВсегоДанных;
		КонецЕсли;
		Результат.НачалоПроцедурыОбработчика = ТекущаяУниверсальнаяДатаВМиллисекундах();
		ОбщегоНазначения.ВыполнитьМетодКонфигурации(КонтекстОбработчика.ИмяОбработчика, ПараметрыВызова);
		Результат.ЗавершениеПроцедурыОбработчика = ТекущаяУниверсальнаяДатаВМиллисекундах();
		
		Результат.Параметры = КонтекстОбработчика.Параметры;
		Результат.ПараметрыОбработчикаОбновления = ПараметрыСеанса.ПараметрыОбработчикаОбновления;
		
		ПроверитьВложеннуюТранзакциюПриВыполненииОтложенногоОбработчика(КонтекстОбработчика, Результат);
		
		ПоместитьВоВременноеХранилище(Результат, АдресРезультата);
		ОтключитьОбновлениеКлючейДоступа(Ложь, ПодсистемаСуществует);
		ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(НовыеПараметрыОбработчикаОбновления());
	Исключение
		ПроверитьВложеннуюТранзакциюПриВыполненииОтложенногоОбработчика(КонтекстОбработчика, Результат);
		
		ОтключитьОбновлениеКлючейДоступа(Ложь, ПодсистемаСуществует);
		ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(НовыеПараметрыОбработчикаОбновления());
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Завершить выполнение отложенного обработчика.
// Вызывается автоматически в основном потоке, после завершения выполнения ВыполнитьОтложенныйОбработчик().
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  АдресРезультата - Строка - адрес временного хранилища с результатом из ВыполнитьОтложенныйОбработчик().
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//
Процедура ЗавершитьВыполнениеОтложенногоОбработчика(КонтекстОбработчика, АдресРезультата) Экспорт
	
	КонтекстОбработчика.Вставить("ПропуститьОтменуТранзакций");
	
	НачатьТранзакцию();
	Попытка
		ОбработчикОбновления = ОбработчикОбновления(КонтекстОбработчика.ИмяОбработчика);
		УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика, "ОбработкаПорцииЗавершена", Истина);
		
		ЗагрузитьРезультатВыполненияОбработчика(КонтекстОбработчика, АдресРезультата);
		ПараметрыСеанса.ПараметрыОбработчикаОбновления = КонтекстОбработчика.ПараметрыОбработчикаОбновления;
		
		Если КонтекстОбработчика.ЗапускВыполненБезОшибок Тогда
			ПослеЗапускаПроцедурыОбработкиДанных(КонтекстОбработчика, КонтекстОбработчика.ИмяОбработчика);
		КонецЕсли;
		
		ЗавершитьПроцедуруОбработкиДанных(КонтекстОбработчика, КонтекстОбработчика.ИмяОбработчика);
		ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(НовыеПараметрыОбработчикаОбновления());
		
		Если ОбработчикОбновления.Многопоточный Тогда
			ЗавершитьВыполнениеМногопоточногоОбработчика(КонтекстОбработчика, КонтекстОбработчика.ИмяОбработчика);
		КонецЕсли;
		
		ЗавершитьВыполнениеОтложенногоОбработчикаОбновления(КонтекстОбработчика);
		РассчитатьВремяВыполненияПроцедурыОбработчика(КонтекстОбработчика, КонтекстОбработчика.ИмяОбработчика);
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	КонтекстОбработчика.Удалить("ПропуститьОтменуТранзакций");
	
	Если КонтекстОбработчика.Свойство("ОшибкаПриЗавершенииОбработчика") Тогда
		ВызватьИсключение КонтекстОбработчика.ОшибкаПриЗавершенииОбработчика;
	КонецЕсли;
	
КонецПроцедуры

// Рассчитать время выполнения процедуры обработки данных (не всего обработчика).
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  ИмяОбработчика - Строка
//
Процедура РассчитатьВремяВыполненияПроцедурыОбработчика(КонтекстОбработчика, ИмяОбработчика)
	
	ОбработчикОбновления = ОбработчикОбновления(ИмяОбработчика);
	
	НачалоПроцедурыОбработчика = ?(КонтекстОбработчика.НачалоПроцедурыОбработчика = Неопределено, 0,
		КонтекстОбработчика.НачалоПроцедурыОбработчика);
	ЗавершениеПроцедурыОбработчика = ?(КонтекстОбработчика.ЗавершениеПроцедурыОбработчика = Неопределено, 0,
		КонтекстОбработчика.ЗавершениеПроцедурыОбработчика);
	ПереданоДанныхДляОбработки = ?(КонтекстОбработчика.ПереданоДанныхДляОбработки = Неопределено, 0,
		КонтекстОбработчика.ПереданоДанныхДляОбработки);
	ДлительностьПроцедурыОбработчика = ЗавершениеПроцедурыОбработчика - НачалоПроцедурыОбработчика;
	НачалоВыполнения = '00010101' + НачалоПроцедурыОбработчика / 1000;
	ЗавершениеВыполнения = '00010101' + ЗавершениеПроцедурыОбработчика / 1000;
	СтатистикаВыполнения = ОбработчикОбновления.СтатистикаВыполнения.Получить();
	СтатистикаНачало = СтатистикаВыполнения["НачалоПроцедурыОбработчика"];
	СтатистикаЗавершение = СтатистикаВыполнения["ЗавершениеПроцедурыОбработчика"];
	СтатистикаДанныхДляОбработки = СтатистикаВыполнения["ПереданоДанныхДляОбработки"];
	СтатистикаДлительность = СтатистикаВыполнения["ДлительностьПроцедурыОбработчика"];
	
	Если СтатистикаНачало = Неопределено Тогда
		СтатистикаНачало = Новый Массив;
		СтатистикаВыполнения["НачалоПроцедурыОбработчика"] = СтатистикаНачало;
	КонецЕсли;
	
	Если СтатистикаЗавершение = Неопределено Тогда
		СтатистикаЗавершение = Новый Массив;
		СтатистикаВыполнения["ЗавершениеПроцедурыОбработчика"] = СтатистикаЗавершение;
	КонецЕсли;
	
	Если СтатистикаДанныхДляОбработки = Неопределено Тогда
		СтатистикаДанныхДляОбработки = Новый Массив;
		СтатистикаВыполнения["ПереданоДанныхДляОбработки"] = СтатистикаДанныхДляОбработки;
	КонецЕсли;
	
	Если СтатистикаДлительность = Неопределено Тогда
		СтатистикаДлительность = Новый Массив;
		СтатистикаВыполнения["ДлительностьПроцедурыОбработчика"] = СтатистикаДлительность;
	КонецЕсли;
	
	СтатистикаНачало.Добавить(НачалоВыполнения);
	СтатистикаЗавершение.Добавить(ЗавершениеВыполнения);
	СтатистикаДанныхДляОбработки.Добавить(ПереданоДанныхДляОбработки);
	СтатистикаДлительность.Добавить(ДлительностьПроцедурыОбработчика);
	
	УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика,
		"СтатистикаВыполнения",
		Новый ХранилищеЗначения(СтатистикаВыполнения));
	
КонецПроцедуры

// Обработчик аварийного завершения выполнения потока отложенного обновления.
//
// Параметры:
//  Поток - см. НовоеОписаниеПотоков
//  ИнформацияОбОшибке - ИнформацияОбОшибке - описание возникшей ошибки.
//
Процедура ПриАварийномЗавершенииПотокаОтложенногоОбработчика(Поток, ИнформацияОбОшибке) Экспорт
	
	ОбработчикОбновления = ОбработчикОбновления(Поток.ПараметрыПроцедуры.ИмяОбработчика);
	УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика, "ОбработкаПорцииЗавершена", Истина);
	ОбработатьИсключениеОбработчика(Поток.ПараметрыПроцедуры, ОбработчикОбновления, ИнформацияОбОшибке);
	
	Если ОбработчикОбновления.Многопоточный Тогда
		ОтменитьОбработкуДанныхМногопоточногоОбработчика(Поток, ОбработчикОбновления);
	КонецЕсли;
	
	КонтекстОбработчика = Поток.ПараметрыПроцедурыЗавершения;
	ЗавершитьВыполнениеОтложенногоОбработчикаОбновления(КонтекстОбработчика);
	
КонецПроцедуры

// Обработчик отмены выполнения потока.
//
// Параметры:
//  Поток - см. НовоеОписаниеПотоков
//
Процедура ПриОтменеПотокаОтложенногоОбработчика(Поток) Экспорт
	
	ОбработчикОбновления = ОбработчикОбновления(Поток.ПараметрыПроцедуры.ИмяОбработчика);
	УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика, "ОбработкаПорцииЗавершена", Истина);
	
	Если ОбработчикОбновления.Статус = Перечисления.СтатусыОбработчиковОбновления.Выполняется Тогда
		УстановитьСтатусОбработчика(ОбработчикОбновления.ИмяОбработчика, "НеВыполнялся");
	КонецЕсли;
	
	Если ОбработчикОбновления.Многопоточный Тогда
		ОтменитьОбработкуДанныхМногопоточногоОбработчика(Поток, ОбработчикОбновления);
	КонецЕсли;
	
КонецПроцедуры

// Загрузить результат выполнения обработчика из временного хранилища в контекст обработчика обновления.
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  АдресРезультата - Строка - адрес результата во временном хранилище.
//
Процедура ЗагрузитьРезультатВыполненияОбработчика(КонтекстОбработчика, АдресРезультата)
	
	Если АдресРезультата <> Неопределено Тогда
		Результат = ПолучитьИзВременногоХранилища(АдресРезультата); // см. НовыйРезультатВыполненияОтложенногоОбработчика
	Иначе
		Результат = Неопределено;
	КонецЕсли;
	
	Если Результат <> Неопределено Тогда
		Если КонтекстОбработчика.ЗаписыватьВЖурнал Тогда
			КонтекстОбработчика.ДетальноеОписаниеОбработчика.Параметры = Результат.Параметры;
		КонецЕсли;
		
		КонтекстОбработчика.ЕстьНеЗакрытыеТранзакции = Результат.ЕстьНеЗакрытыеТранзакции;
		КонтекстОбработчика.ЗавершениеПроцедурыОбработчика = Результат.ЗавершениеПроцедурыОбработчика;
		КонтекстОбработчика.ИнформацияОбОшибке = Результат.ИнформацияОбОшибке;
		КонтекстОбработчика.НачалоПроцедурыОбработчика = Результат.НачалоПроцедурыОбработчика;
		КонтекстОбработчика.ПереданоДанныхДляОбработки = Результат.ПереданоДанныхДляОбработки;
		КонтекстОбработчика.Параметры = Результат.Параметры;
		КонтекстОбработчика.ПараметрыОбработчикаОбновления = Результат.ПараметрыОбработчикаОбновления;
	КонецЕсли;
	
КонецПроцедуры

Процедура УдалитьВсеПотокиОбновления()
	
	НаборЗаписей = РегистрыСведений.ПотокиОбновления.СоздатьНаборЗаписей();
	НаборЗаписей.Записать();
	
КонецПроцедуры

// Контекст выполнения обработчика обновления.
//
// Возвращаемое значение:
//  Структура - описание контекста (сериализуется для возможности передачи в фоновое задание):
//   * ВыполнитьОбработчик - Булево - если Истина, то обработчик готов к выполнению.
//   * ДетальноеОписаниеОбработчика - см. ПодготовитьДетальнуюИнформациюОХодеОбновления
//   * ЗавершениеПроцедурыОбработчика - Число - завершение процедуры обработки данных.
//   * ЗаписыватьВЖурнал - Булево - см. Константы.ДетализироватьОбновлениеИБВЖурналеРегистрации.
//   * ЗапускВыполненБезОшибок - Булево - если Истина, то при запуске обработчика не возникло исключений.
//   * ИдентификаторОбработчика - УникальныйИдентификатор - идентификатор обработчика обновления.
//   * ИмяОбработчика - Строка - имя обработчика обновления.
//   * ИндексОписанияЦиклаОбновления - Число - индекс элемента плана обновления.
//   * ИндексТекущегоЦиклаОбновления - Число - индекс текущего элемента плана обновления.
//   * НачалоОбработкиДанных - Дата - время начала выполнения обработчика обновления.
//   * НачалоПроцедурыОбработчика - Число - начало процедуры обработки данных.
//   * ПереданоДанныхДляОбработки - Число - количество объектов, переданных для обработки.
//   * ПараллельныйРежим - Булево - признак параллельного режима выполнения обработчика обновления.
//   * Параметры - Структура - параметры обработчика обновления со свойствами:
//      ** ОбновляемыеДанные - см. НоваяПорцияДляОбновления
//   * ПараметрыОбновления - Структура - описание параметров обновления.
//   * ПараметрыОбработчикаОбновления - см. ПараметрыСеанса.ПараметрыОбработчикаОбновления
//   * ПропуститьПроверкуОбработанныхДанных - Булево - пропустить проверку в подчиненном узле РИБ.
//   * ТекущаяИтерацияОбновления - Число - номер текущей итерации обновления.
//   * ТранзакцияАктивнаНаНачалоВыполнения - Булево - состояние активности транзакции перед выполнением обработчика.
//   * ВерсияПодсистемыНаНачалоОбновления - Строка - версия подсистемы, к которой относится обработчик обновления,
//                                                   на момент начала обновления.
//
Функция НовыйКонтекстОбработчика()
	
	КонтекстОбработчика = Новый Структура;
	
	КонтекстОбработчика.Вставить("ВыполнитьОбработчик", Ложь);
	КонтекстОбработчика.Вставить("ДетальноеОписаниеОбработчика");
	КонтекстОбработчика.Вставить("ЕстьНеЗакрытыеТранзакции", Ложь);
	КонтекстОбработчика.Вставить("ЗавершениеПроцедурыОбработчика");
	КонтекстОбработчика.Вставить("ЗаписыватьВЖурнал");
	КонтекстОбработчика.Вставить("ЗапускВыполненБезОшибок", Ложь);
	КонтекстОбработчика.Вставить("ИдентификаторОбработчика");
	КонтекстОбработчика.Вставить("ИмяОбработчика");
	КонтекстОбработчика.Вставить("ИндексОписанияЦиклаОбновления");
	КонтекстОбработчика.Вставить("ИндексТекущегоЦиклаОбновления");
	КонтекстОбработчика.Вставить("ИнформацияОбОшибке");
	КонтекстОбработчика.Вставить("НачалоОбработкиДанных");
	КонтекстОбработчика.Вставить("НачалоПроцедурыОбработчика");
	КонтекстОбработчика.Вставить("ПереданоДанныхДляОбработки");
	КонтекстОбработчика.Вставить("ПараллельныйРежим");
	КонтекстОбработчика.Вставить("Параметры");
	КонтекстОбработчика.Вставить("ПараметрыОбновления");
	КонтекстОбработчика.Вставить("ПараметрыОбработчикаОбновления");
	КонтекстОбработчика.Вставить("ПропуститьПроверкуОбработанныхДанных", Ложь);
	КонтекстОбработчика.Вставить("ТекущаяИтерацияОбновления");
	КонтекстОбработчика.Вставить("ТранзакцияАктивнаНаНачалоВыполнения");
	КонтекстОбработчика.Вставить("ВерсияПодсистемыНаНачалоОбновления");
	
	Возврат КонтекстОбработчика;
	
КонецФункции

// Дополнить контекст обработчика полями для многопоточного обработчика.
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//
Процедура ДополнитьКонтекстМногопоточногоОбработчика(КонтекстОбработчика)
	
	КонтекстОбработчика.Параметры.Вставить("ОбновляемыеДанные");
	
КонецПроцедуры

// Результат выполнения отложенного обработчика обновления для передачи в процедуру завершения
// его выполнения в управляющем потоке.
//
// Возвращаемое значение:
//  Структура:
//   * ЕстьНеЗакрытыеТранзакции - Булево - признак наличия не закрытых транзакций в самом обработчике.
//   * ЗавершениеПроцедурыОбработчика - Число - время завершения выполнения процедуры обработчика обновления.
//   * ИнформацияОбОшибке - Строка - описание ошибки (если была).
//   * НачалоПроцедурыОбработчика - Число - время начала выполнения процедуры обработчика обновления.
//   * ПереданоДанныхДляОбработки - Число - количество объектов, переданных для обработки.
//   * Параметры - Структура - параметры, переданные в обработчик обновления.
//   * ПараметрыОбработчикаОбновления - ФиксированнаяСтруктура - значение параметра сеанса
//                                      ПараметрыОбработчикаОбновления.
//
Функция НовыйРезультатВыполненияОтложенногоОбработчика()
	
	Результат = Новый Структура;
	Результат.Вставить("ЕстьНеЗакрытыеТранзакции", Ложь);
	Результат.Вставить("ЗавершениеПроцедурыОбработчика");
	Результат.Вставить("ИнформацияОбОшибке");
	Результат.Вставить("НачалоПроцедурыОбработчика");
	Результат.Вставить("ПереданоДанныхДляОбработки");
	Результат.Вставить("Параметры");
	Результат.Вставить("ПараметрыОбработчикаОбновления");
	
	Возврат Результат;
	
КонецФункции

// Количество потоков обновления по умолчанию.
//
// Возвращаемое значение:
//  Число - количество потоков равное 1 (для обратной совместимости), если не переопределено в
//          ОбновлениеИнформационнойБазыПереопределяемый.ПриОпределенииНастроек().
//
Функция КоличествоПотоковОбновленияИнформационнойБазыПоУмолчанию()
	
	Параметры = НастройкиПодсистемы();
	Возврат Параметры.КоличествоПотоковОбновленияИнформационнойБазыПоУмолчанию;
	
КонецФункции

// Определить приоритет обновления.
//
// Параметры:
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//
// Возвращаемое значение:
//  Булево - Истина - приоритет на обработку данных, Ложь - приоритет на работу пользователей.
//
Функция ФорсироватьОбновление(СведенияОбОбновлении)
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		ПараметрЗапускаКлиента = СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере().Получить("ПараметрЗапуска");
		Если СтрНайти(НРег(ПараметрЗапускаКлиента), НРег("ФорсироватьОтложенноеОбновление")) > 0 Тогда
			Возврат Истина;
		Иначе
			Возврат СведенияОбОбновлении.УправлениеОтложеннымОбновлением.Свойство("ФорсироватьОбновление");
		КонецЕсли;
	Иначе
		Приоритет = Неопределено;
		ИнтеграцияПодсистемБСП.ПриПолученииПриоритетаОбновления(Приоритет);
		
		Если Приоритет = "РаботаПользователей" Тогда
			Возврат Ложь;
		ИначеЕсли Приоритет = "ОбработкаДанных" Тогда
			Возврат Истина;
		Иначе
			Возврат СведенияОбОбновлении.УправлениеОтложеннымОбновлением.Свойство("ФорсироватьОбновление");
		КонецЕсли;
	КонецЕсли;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Механизм работы с потоками.

Процедура СохранитьПотокОбновления(Поток)
	
	НаборЗаписей = РегистрыСведений.ПотокиОбновления.СоздатьНаборЗаписей();
	Идентификатор = Поток.ИдентификаторПотока;
	Если Не ЗначениеЗаполнено(Идентификатор) Тогда
		Идентификатор = Новый УникальныйИдентификатор;
	КонецЕсли;
	НаборЗаписей.Отбор.ИдентификаторПотока.Установить(Идентификатор);
	НаборЗаписей.Прочитать();
	
	Если НаборЗаписей.Количество() = 0 Тогда
		Запись = НаборЗаписей.Добавить();
	Иначе
		Запись = НаборЗаписей[0];
	КонецЕсли;
	
	ЗаполнитьЗначенияСвойств(Запись, Поток, , "ИдентификаторПотока,ПараметрыПроцедуры,ПараметрыПроцедурыЗавершения");
	Запись.ИдентификаторПотока = Идентификатор;
	Запись.ПараметрыПроцедуры = Новый ХранилищеЗначения(Поток.ПараметрыПроцедуры);
	Запись.ПараметрыПроцедурыЗавершения = Новый ХранилищеЗначения(Поток.ПараметрыПроцедурыЗавершения);
	
	НаборЗаписей.Записать();
	
КонецПроцедуры

Процедура УдалитьПотокОбновления(ИдентификаторПотока)
	
	НаборЗаписей = РегистрыСведений.ПотокиОбновления.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.ИдентификаторПотока.Установить(ИдентификаторПотока);
	НаборЗаписей.Записать();
	
КонецПроцедуры

Функция ПотокиОбновления()
	
	ТаблицаПотоков = НовоеОписаниеПотоков();
	
	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ПотокиОбновления.АдресРезультата КАК АдресРезультата,
		|	ПотокиОбновления.Группа КАК Группа,
		|	ПотокиОбновления.Наименование КАК Наименование,
		|	ПотокиОбновления.ПараметрыПроцедуры КАК ПараметрыПроцедуры,
		|	ПотокиОбновления.ПараметрыПроцедурыЗавершения КАК ПараметрыПроцедурыЗавершения,
		|	ПотокиОбновления.ПриоритетЗавершения КАК ПриоритетЗавершения,
		|	ПотокиОбновления.ИдентификаторЗадания КАК ИдентификаторЗадания,
		|	ПотокиОбновления.ИдентификаторПотока КАК ИдентификаторПотока
		|ИЗ
		|	РегистрСведений.ПотокиОбновления КАК ПотокиОбновления";
	Результат = Запрос.Выполнить().Выгрузить();
	Для Каждого Поток Из Результат Цикл
		Строка = ТаблицаПотоков.Добавить();
		ЗаполнитьЗначенияСвойств(Строка, Поток, , "ПараметрыПроцедуры,ПараметрыПроцедурыЗавершения");
		Если Не ЗначениеЗаполнено(Строка.АдресРезультата) Тогда
			Строка.АдресРезультата = Неопределено;
		КонецЕсли;
		Строка.ПараметрыПроцедурыЗавершения = Поток.ПараметрыПроцедурыЗавершения.Получить();
		Строка.ПараметрыПроцедуры = Поток.ПараметрыПроцедуры.Получить();
	КонецЦикла;
	Возврат ТаблицаПотоков;
	
КонецФункции

// Выполнить указанный поток.
//
// Параметры:
//  Группы - Соответствие
//  Поток - СтрокаТаблицыЗначений из см. НовоеОписаниеПотоков
//  ИдентификаторФормы - УникальныйИдентификатор - идентификатор формы, если есть.
//
Процедура ВыполнитьПоток(Группы, Поток, ИдентификаторФормы = Неопределено)
	
	ОписаниеПотока = Группы[Поток.Группа];
	
	Если Не ПустаяСтрока(ОписаниеПотока.Процедура) И Поток.ПараметрыПроцедуры <> Неопределено Тогда
		ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(ИдентификаторФормы);
		ПараметрыВыполнения.НаименованиеФоновогоЗадания = Поток.Наименование;
		ПараметрыВыполнения.ОжидатьЗавершение = 0;
		
		Если ИдентификаторФормы = Неопределено Тогда
			ПараметрыВыполнения.АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, Новый УникальныйИдентификатор);
		КонецЕсли;
		
		РезультатЗапуска = ДлительныеОперации.ВыполнитьВФоне(ОписаниеПотока.Процедура,
			Поток.ПараметрыПроцедуры,
			ПараметрыВыполнения);
		
		Поток.АдресРезультата = РезультатЗапуска.АдресРезультата;
		Статус = РезультатЗапуска.Статус;
		
		Если Статус = "Выполняется" Тогда
			Поток.ИдентификаторЗадания = РезультатЗапуска.ИдентификаторЗадания;
		ИначеЕсли Статус <> "Выполняется" И Статус <> "Выполнено" Тогда
			ВызватьИсключение РезультатЗапуска.КраткоеПредставлениеОшибки;
		КонецЕсли;
	КонецЕсли;
	
	СохранитьПотокОбновления(Поток);
	
КонецПроцедуры

// Завершает выполнение потоков, которые завершили выполнение своих фоновых заданий.
//
// Параметры:
//  Потоки - см. НовоеОписаниеПотоков
//  Группы - Соответствие
//
// Возвращаемое значение:
//  Булево - Истина, если завершился один или более потоков, иначе - Ложь.
//
Функция ЗавершитьПотокиВыполнившиеФЗ(Потоки, Группы)
	
	ЕстьЗавершенныеПотоки = Ложь;
	Потоки.Сортировать("ПриоритетЗавершения Убыв");
	Индекс = Потоки.Количество() - 1;
	
	Пока Индекс >= 0 Цикл
		Поток = Потоки[Индекс];
		ОписаниеПотока = Группы[Поток.Группа];
		ИдентификаторЗадания = Поток.ИдентификаторЗадания;
		
		Если ЗначениеЗаполнено(ИдентификаторЗадания) Тогда
			Попытка
				ЗаданиеВыполнено = ДлительныеОперации.ЗаданиеВыполнено(ИдентификаторЗадания);
			Исключение
				ИнформацияОбОшибке = ИнформацияОбОшибке();
				ЗаданиеВыполнено = Неопределено;
				
				Если Не ПустаяСтрока(ОписаниеПотока.ПриАварийномЗавершении) Тогда
					ПараметрыВызова = Новый Массив;
					ПараметрыВызова.Добавить(Поток);
					ПараметрыВызова.Добавить(ИнформацияОбОшибке);
					
					ОбщегоНазначения.ВыполнитьМетодКонфигурации(ОписаниеПотока.ПриАварийномЗавершении, ПараметрыВызова);
				Иначе
					ВызватьИсключение;
				КонецЕсли;
			КонецПопытки;
		КонецЕсли;
		
		Если Не ЗначениеЗаполнено(ИдентификаторЗадания) Или ЗаданиеВыполнено <> Ложь Тогда
			Выполнять = Не ПустаяСтрока(ОписаниеПотока.ПроцедураЗавершения)
			          И Поток.ПараметрыПроцедурыЗавершения <> Неопределено
			          И (Не ЗначениеЗаполнено(ИдентификаторЗадания) Или ЗаданиеВыполнено = Истина)
			          И Поток.АдресРезультата <> Неопределено;
			
			Если Выполнять Тогда
				ПараметрыВызова = Новый Массив;
				ПараметрыВызова.Добавить(Поток.ПараметрыПроцедурыЗавершения);
				ПараметрыВызова.Добавить(Поток.АдресРезультата);
				
				ОбщегоНазначения.ВыполнитьМетодКонфигурации(ОписаниеПотока.ПроцедураЗавершения, ПараметрыВызова);
			КонецЕсли;
			
			Если Поток.АдресРезультата <> Неопределено Тогда
				УдалитьИзВременногоХранилища(Поток.АдресРезультата);
			КонецЕсли;
			УдалитьПотокОбновления(Поток.ИдентификаторПотока);
			Потоки.Удалить(Поток);
			ЕстьЗавершенныеПотоки = Истина;
		КонецЕсли;
		
		Индекс = Индекс - 1;
	КонецЦикла;
	
	Возврат ЕстьЗавершенныеПотоки;
	
КонецФункции

// Ожидать завершение всех потоков.
//
// Параметры:
//  Группы - Соответствие
//
Процедура ОжидатьЗавершениеВсехПотоков(Группы)
	
	Потоки = ПотокиОбновления();
	
	Пока Потоки.Количество() > 0 Цикл
		Если Не ЗавершитьПотокиВыполнившиеФЗ(Потоки, Группы) Тогда
			ОжидатьЗавершениеПотока(Потоки[0]);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Ожидать завершение любого потока.
//
// Параметры:
//  Группы - Соответствие
//
Процедура ОжидатьЗавершениеЛюбогоПотока(Группы)
	
	Потоки = ПотокиОбновления();
	КоличествоПотоков = Потоки.Количество();
	
	Пока КоличествоПотоков > 0 И Потоки.Количество() >= КоличествоПотоков Цикл
		Если Не ЗавершитьПотокиВыполнившиеФЗ(Потоки, Группы) Тогда
			ОжидатьЗавершениеПотока(Потоки[0]);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Ожидать, пока количество выполняющихся потоков не станет меньше максимального.
//
// Параметры:
//  Группы - Соответствие
//
Процедура ОжидатьСвободныйПоток(Группы)
	
	МаксимумПотоков = КоличествоПотоковОбновленияИнформационнойБазы();
	
	Потоки = ПотокиОбновления();
	
	Пока Потоки.Количество() >= МаксимумПотоков Цикл
		Если ЗавершитьПотокиВыполнившиеФЗ(Потоки, Группы) Тогда
			Продолжить;
		КонецЕсли;
		
		ОжидатьЗавершениеПотока(Потоки[0]);
		МаксимумПотоков = КоличествоПотоковОбновленияИнформационнойБазы();
	КонецЦикла;
	
КонецПроцедуры

// Отменить выполнение потоков, если они активны.
//
// Параметры:
//  Группы - Соответствие
//
Процедура ОтменитьВыполнениеВсехПотоков(Группы) Экспорт
	
	Потоки = ПотокиОбновления();
	Для Каждого Поток Из Потоки Цикл
		ОписаниеПотока = Группы[Поток.Группа];
		
		Если ЗначениеЗаполнено(Поток.ИдентификаторЗадания) Тогда
			ДлительныеОперации.ОтменитьВыполнениеЗадания(Поток.ИдентификаторЗадания);
		КонецЕсли;
		
		Если ОписаниеПотока.ПриОтменеПотока <> Неопределено Тогда
			ПараметрыВызова = Новый Массив;
			ПараметрыВызова.Добавить(Поток);
			ОбщегоНазначения.ВыполнитьМетодКонфигурации(ОписаниеПотока.ПриОтменеПотока, ПараметрыВызова);
		КонецЕсли;
		
		УдалитьПотокОбновления(Поток.ИдентификаторПотока);
	КонецЦикла;
	
КонецПроцедуры

// Описание группы потоков.
//
// Возвращаемое значение:
//  Структура - общее описание потоков со следующими полями:
//   * Процедура - Строка - имя процедуры, выполняемой в фоновом задании. Объявление:
//                 ИмяПроцедуры(ОписаниеПроцедуры, АдресРезультата), где:
//                   ОписаниеПроцедуры - Структура - описание процедуры заполнения.
//                   АдресРезультата - Строка - адрес временного хранилища для сохранения результата выполнения.
//   * ПроцедураЗавершения - Строка - имя процедуры, выполняемой, после завершения работы фонового задания. Объявление:
//                           ПроцедураЗавершения(ОписаниеПроцедуры, АдресРезультата, ДополнительныеПараметры), где:
//                             ОписаниеПроцедуры - Структура - описание процедуры заполнения.
//                             АдресРезультата - Строка - адрес временного хранилища с результатом выполнения.
//                             ДополнительныеПараметры - Произвольный - дополнительный параметр.
//   * ПриАварийномЗавершении - Строка - обработчик аварийного завершения потока. Объявление:
//                              ПриАварийномЗавершении(Поток, ИнформацияОбОшибке, ДополнительныеПараметры), где:
//                                Поток - см. НовоеОписаниеПотоков
//                                ИнформацияОбОшибке - ИнформацияОбОшибке - описание возникшей ошибки.
//                                ДополнительныеПараметры - Произвольный - дополнительный параметр.
//   * ПриОтменеПотока - Строка - обработчик отмены потока. Объявление:
//                       ПриОтменеПотока(Поток, ДополнительныеПараметры), где:
//                         Поток - см. НовоеОписаниеПотоков
//                         ДополнительныеПараметры - Произвольный - дополнительный параметр.
//
Функция НовоеОписаниеГруппыПотоков()
	
	Описание = Новый Структура;
	Описание.Вставить("Процедура");
	Описание.Вставить("ПроцедураЗавершения");
	Описание.Вставить("ПриАварийномЗавершении");
	Описание.Вставить("ПриОтменеПотока");
	
	Возврат Описание;
	
КонецФункции

// Описание группы потоков.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//     * Группы - Соответствие из КлючИЗначение - описание групп потоков, где:
//       ** Ключ - Строка - имя группы.
//       ** Значение - см. НовоеОписаниеГруппыПотоков
//     * Потоки - ТаблицаЗначений - описание потоков со следующими колонками:
//       ** Наименование - Строка - произвольное наименование потока (используется в наименовании фонового задания).
//       ** Группа - Строка - имя группы с описанием потока.
//       ** ИдентификаторЗадания - УникальныйИдентификатор - уникальный идентификатор фонового задания.
//       ** ПараметрыПроцедуры - Произвольный - параметры для Процедура.
//       ** ПараметрыПроцедурыЗавершения - Произвольный - параметры для ПроцедураЗавершения.
//       ** АдресРезультата - Строка - адрес временного хранилища для сохранения результата выполнения фонового задания.
//
Функция НовоеОписаниеПотоков()
	
	Потоки = Новый ТаблицаЗначений;
	Колонки = Потоки.Колонки;
	Колонки.Добавить("Наименование");
	Колонки.Добавить("Группа");
	Колонки.Добавить("ПриоритетЗавершения", Новый ОписаниеТипов("Число"));
	Колонки.Добавить("ИдентификаторЗадания");
	Колонки.Добавить("ИдентификаторПотока");
	Колонки.Добавить("ПараметрыПроцедуры");
	Колонки.Добавить("ПараметрыПроцедурыЗавершения");
	Колонки.Добавить("АдресРезультата");
	
	Возврат Потоки;
	
КонецФункции

// Ожидать завершение потока в течение указанной длительности.
//
// Параметры:
//   Поток - СтрокаТаблицыЗначений из см. НовоеОписаниеПотоков
//   Длительность - Число - максимальная длительность ожидания в секундах.
//
// Возвращаемое значение:
//  Булево - Истина - поток выполнен, Ложь - поток еще выполняется.
//
Функция ОжидатьЗавершениеПотока(Поток, Длительность = 1)
	
	Если ЗначениеЗаполнено(Поток.ИдентификаторЗадания) Тогда
		Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Поток.ИдентификаторЗадания);
		
		Если Задание <> Неопределено Тогда
			Задание = Задание.ОжидатьЗавершенияВыполнения(Длительность);
			ЗаданиеЗавершено = (Задание.Состояние <> СостояниеФоновогоЗадания.Активно);
			Возврат ЗаданиеЗавершено;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Механизм многопоточного выполнения обработчика обновления.

// Найти порцию данных для потока обработчика обновления.
//
// Параметры:
//  ПараметрыПоиска - см. НовыеПараметрыПоискаПорции
//  АдресРезультата - Строка - адрес результата выполнения процедуры. Возвращается таблица значений.
//
Процедура НайтиПорциюДляОбновления(ПараметрыПоиска, АдресРезультата) Экспорт
	
	ПараметрыВыборки = ПараметрыПоиска.ПараметрыВыборки;
	ПроверитьПараметрыВыборки(ПараметрыВыборки);
	ПараметрыВыборки.МаксимумВыборки = ОбновлениеИнформационнойБазы.МаксимальноеКоличествоЗаписейВВыборке();
	ПоляУпорядочивания = ПоляУпорядочиванияПриПоискеПорций(ПараметрыПоиска);
	ПараметрыВыборки.ПоляУпорядочивания = ПоляУпорядочивания;
	ПараметрыВыборки.ОптимизироватьВыборкуПоСтраницам = Не ЕстьУпорядочиваниеПоВнешнимТаблицам(ПоляУпорядочивания);
	Максимум = ОбновлениеИнформационнойБазы.МаксимальноеКоличествоЗаписейВВыборке();
	ПараметрыОбхода = ПараметрыОбходаДанныхДляОбновления(ПараметрыПоиска);
	ПоискСНачала = ПараметрыПоиска.ПоследняяВыбраннаяЗапись = Неопределено И ПараметрыПоиска.ПерваяЗапись = Неопределено;
	
	Если Не ПоискСНачала Тогда
		СледующиеПараметрыОбхода(ПараметрыОбхода, Ложь);
	КонецЕсли;

	ИтераторОбхода = ТекущиеПараметрыОбхода(ПараметрыОбхода);
	РезультатПоиска = НовыйРезультатПоискаПорции();
	НаборДанных = НовыйНаборДанныхДляОбновления();
	РезультатПоиска.НаборДанных = НаборДанных;
	ПараметрыВыборки = ПараметрыПоиска.ПараметрыВыборки;
	ДополнительныеИсточникиДанных = ПараметрыВыборки.ДополнительныеИсточникиДанных;
	Очередь = ПараметрыПоиска.Очередь;
	
	Пока ИтераторОбхода <> Неопределено Цикл
		СсылочныйОбъект = ИтераторОбхода.СсылочныйОбъект;
		ТабличныйОбъект = ИтераторОбхода.ТабличныйОбъект;
		УстановитьГраницуНачалаВыборки(ПараметрыПоиска, ИтераторОбхода.СсылочныйИндекс, ИтераторОбхода.ТабличныйИндекс);
		УстановитьГраницуКонцаВыборки(ПараметрыПоиска, СсылочныйОбъект, ТабличныйОбъект);
		МаксимумВыборки = ПараметрыВыборки.МаксимумВыборки;
		ПараметрыВыборки.ДополнительныеИсточникиДанных = ОбновлениеИнформационнойБазы.ИсточникиДанных(
			ДополнительныеИсточникиДанных,
			СсылочныйОбъект,
			ТабличныйОбъект);
		
		// Так как параметр сеанса не устанавливается на время выборки данных, то добавляем в параметры выборки
		// текущие параметры обработчика обновления.
		Если Не ПараметрыВыборки.Свойство("ПараметрыОбработчикаОбновления") Тогда
			ПараметрыВыборки.Вставить("ПараметрыОбработчикаОбновления", ПараметрыПоиска.КонтекстОбработчика.ПараметрыОбработчикаОбновления);
		КонецЕсли;
		Данные = ВыбратьДанныеПорции(ПараметрыВыборки, Очередь, СсылочныйОбъект, ТабличныйОбъект);
		
		Если ПараметрыВыборки.ПараметрыОбработчикаОбновления.ОбработаныАктуальныеДанные = Истина Тогда
			РезультатПоиска.ОбработаныАктуальныеДанные = Истина;
		ИначеЕсли ПараметрыВыборки.ПараметрыОбработчикаОбновления.ОбработанныеТаблицыРегистраторов <> Неопределено Тогда
			РезультатПоиска.ОбработанныеТаблицыРегистраторов = ПараметрыВыборки.ПараметрыОбработчикаОбновления.ОбработанныеТаблицыРегистраторов;
		КонецЕсли;
		
		Количество = Данные.Количество();
		РезультатПоиска.Количество = РезультатПоиска.Количество + Количество;
		ПараметрыВыборки.МаксимумВыборки = ПараметрыВыборки.МаксимумВыборки - Количество;
		
		Если Количество > 0 Тогда
			ЗаписьНабора = НаборДанных.Добавить();
			ЗаписьНабора.СсылочныйОбъект = СсылочныйОбъект;
			ЗаписьНабора.ТабличныйОбъект = ТабличныйОбъект;
			ЗаписьНабора.Данные = Данные;
		КонецЕсли;
		
		Если РезультатПоиска.Количество < Максимум Тогда
			СледующиеПараметрыОбхода(ПараметрыОбхода, Количество = МаксимумВыборки);
			ИтераторОбхода = ТекущиеПараметрыОбхода(ПараметрыОбхода);
		Иначе
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	ПараметрыВыборки.ДополнительныеИсточникиДанных = ДополнительныеИсточникиДанных;
	РезультатПоиска.ПоискЗавершен = (ИтераторОбхода = Неопределено);
	ПоместитьВоВременноеХранилище(РезультатПоиска, АдресРезультата);
	
КонецПроцедуры

// Проверить корректность заполнения параметров выборки данных обработчика обновления.
//
// Параметры:
//   ПараметрыВыборки - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки
//
Процедура ПроверитьПараметрыВыборки(ПараметрыВыборки)
	
	СпособВыборки = ПараметрыВыборки.СпособВыборки;
	ИзвестныйСпособ = (СпособВыборки = ОбновлениеИнформационнойБазы.СпособВыборкиИзмеренияНезависимогоРегистраСведений())
	              Или (СпособВыборки = ОбновлениеИнформационнойБазы.СпособВыборкиРегистраторыРегистра())
	              Или (СпособВыборки = ОбновлениеИнформационнойБазы.СпособВыборкиСсылки());
	Если Не ИзвестныйСпособ Тогда
		ШаблонСообщения = НСтр(
			"ru = 'Укажите способ выборки в процедуре регистрации данных к обновлению.
			|Указывается в ""%1"".
			|Сейчас указан неизвестный способ выборки ""%2"".'");
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения, СпособВыборки, "Параметры.ПараметрыВыборки.СпособВыборки");
	КонецЕсли;
	
	ТаблицыУказаны = Не ПустаяСтрока(ПараметрыВыборки.ПолныеИменаОбъектов)
	             Или Не ПустаяСтрока(ПараметрыВыборки.ПолныеИменаРегистров);
	Если Не ТаблицыУказаны Тогда
		ШаблонСообщения = НСтр(
			"ru = 'Укажите обрабатываемые таблицы в процедуре регистрации данных к обновлению.
			|Указывается в ""%1"" и/или
			|""%2"".'");
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения,
			"Параметры.ПараметрыВыборки.ПолныеИменаОбъектов", "Параметры.ПараметрыВыборки.ПолныеИменаРегистров");
	КонецЕсли;
	
КонецПроцедуры

// Установить границу начала выборки порции.
//
// Параметры:
//  ПараметрыПоиска - см. НовыеПараметрыПоискаПорции
//  СсылочныйИндекс - Число - номер итерации по ссылочным объектам.
//  ТабличныйИндекс - Число - номер итерации по табличным объектам.
//
Процедура УстановитьГраницуНачалаВыборки(ПараметрыПоиска, СсылочныйИндекс, ТабличныйИндекс)
	
	ПараметрыВыборки = ПараметрыПоиска.ПараметрыВыборки;
	ПоследняяВыбраннаяЗапись = ПараметрыПоиска.ПоследняяВыбраннаяЗапись;
	ПерваяЗапись = ПараметрыПоиска.ПерваяЗапись;
	
	Если СсылочныйИндекс = 0 И ТабличныйИндекс = 0 Тогда // Выбирается первая страница в цикле выборки.
		ПараметрыВыборки.ПоследняяВыбраннаяЗапись = ПоследняяВыбраннаяЗапись;
		ПараметрыВыборки.ПерваяЗапись = ПерваяЗапись;
	Иначе // Выбираются последующие страницы в цикле выборки (всегда в новом объекте, поэтому сначала).
		ПараметрыВыборки.ПоследняяВыбраннаяЗапись = Неопределено;
		ПараметрыВыборки.ПерваяЗапись = Неопределено;
	КонецЕсли;
	
КонецПроцедуры

// Установить границу конца выборки порции.
//
// Параметры:
//  ПараметрыПоиска - см. НовыеПараметрыПоискаПорции
//  СсылочныйОбъект - Строка - полное имя ссылочного объекта метаданных.
//  ТабличныйОбъект - Строка - полное имя табличного объекта метаданных.
//
Процедура УстановитьГраницуКонцаВыборки(ПараметрыПоиска, СсылочныйОбъект, ТабличныйОбъект)
	
	ПараметрыВыборки = ПараметрыПоиска.ПараметрыВыборки;
	ПоследняяЗапись = ПараметрыПоиска.ПоследняяЗапись;
	ЭтоПоследнийОбъект = ПоследняяЗапись <> Неопределено
	                   И СсылочныйОбъект = ПоследняяЗапись[0].Значение
	                   И ТабличныйОбъект = ПоследняяЗапись[1].Значение;
	
	Если ЭтоПоследнийОбъект Тогда // Последний объект в цикле обхода метаданных (конец выборки).
		ПараметрыВыборки.ПоследняяЗапись = ПоследняяЗапись;
	Иначе // Промежуточная выборка.
		ПараметрыВыборки.ПоследняяЗапись = Неопределено;
	КонецЕсли;
	
КонецПроцедуры

// Выбрать данные порции указанным способом.
//
// Параметры:
//  ПараметрыВыборки - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки
//  Очередь - Число - номер очереди.
//  СсылочныйОбъект - Строка - полное имя ссылочного объекта метаданных.
//  ТабличныйОбъект - Строка - полное имя табличного объекта метаданных.
//
// Возвращаемое значение:
//  ТаблицаЗначений - данные порции.
//
Функция ВыбратьДанныеПорции(ПараметрыВыборки, Очередь, СсылочныйОбъект, ТабличныйОбъект)
	
	СпособВыборки = ПараметрыВыборки.СпособВыборки;
	
	Если СпособВыборки = ОбновлениеИнформационнойБазы.СпособВыборкиИзмеренияНезависимогоРегистраСведений() Тогда
		Данные = ОбновлениеИнформационнойБазы.ВыбратьИзмеренияНезависимогоРегистраСведенийДляОбработки(
			Очередь,
			ТабличныйОбъект,
			ПараметрыВыборки);
	ИначеЕсли СпособВыборки = ОбновлениеИнформационнойБазы.СпособВыборкиРегистраторыРегистра() Тогда
		Данные = ОбновлениеИнформационнойБазы.ВыбратьРегистраторыРегистраДляОбработки(
			Очередь,
			?(ПустаяСтрока(СсылочныйОбъект), Неопределено, СсылочныйОбъект),
			ТабличныйОбъект,
			ПараметрыВыборки);
	ИначеЕсли СпособВыборки = ОбновлениеИнформационнойБазы.СпособВыборкиСсылки() Тогда
		Данные = ОбновлениеИнформационнойБазы.ВыбратьСсылкиДляОбработки(
			Очередь,
			СсылочныйОбъект,
			ПараметрыВыборки);
	КонецЕсли;
	
	Возврат Данные;
	
КонецФункции

// Подготовить параметры обхода данных для обновления.
// По сути найти границу начала выборки (то место, где остановились в последний раз).
//
// Параметры:
//  ПараметрыПоиска - см. НовыеПараметрыПоискаПорции
//
// Возвращаемое значение:
//  Структура - параметры поиска, со следующими полями:
//   СсылочныеОбъекты - Массив - имена ссылочных объектов метаданных.
//   ТабличныеОбъектыВсе - Массив - имена табличных объектов метаданных.
//   ТабличныеОбъектыНачало - Массив - имена табличных объектов метаданных на первой итерации.
//
Функция ПараметрыОбходаДанныхДляОбновления(ПараметрыПоиска)
	
	ПоследняяВыбраннаяЗапись = ПараметрыПоиска.ПоследняяВыбраннаяЗапись;
	ПерваяЗапись = ПараметрыПоиска.ПерваяЗапись;
	ПараметрыВыборки = ПараметрыПоиска.ПараметрыВыборки;
	ПолныеИменаОбъектов = ПараметрыВыборки.ПолныеИменаОбъектов;
	ПолныеИменаРегистров = ПараметрыВыборки.ПолныеИменаРегистров;
	ПолныеИменаРегистровНачало = ПолныеИменаРегистров;
	
	Если ПоследняяВыбраннаяЗапись <> Неопределено Тогда // Продолжение выборки по страницам.
		ПерваяСсылочная = ПоследняяВыбраннаяЗапись[0].Значение;
		ПерваяТабличная = ПоследняяВыбраннаяЗапись[1].Значение;
	ИначеЕсли ПерваяЗапись <> Неопределено Тогда // Повтор выборки (завершившейся аварийно).
		ПерваяСсылочная = ПерваяЗапись[0].Значение;
		ПерваяТабличная = ПерваяЗапись[1].Значение;
	Иначе // Начало выборки (выборка первой страницы).
		ПерваяСсылочная = Неопределено;
		ПерваяТабличная = Неопределено;
	КонецЕсли;
	
	Если Не ПустаяСтрока(ПолныеИменаОбъектов) И Не ПустаяСтрока(ПерваяСсылочная) Тогда // Есть ссылочные объекты.
		// Установить ссылочную часть начала выборки на место, на котором она остановилась в последний раз.
		ПолныеИменаОбъектовМассив = ОстатокМассива(ПолныеИменаОбъектов, ПерваяСсылочная);
	Иначе
		ПолныеИменаОбъектовМассив = СтрРазделитьСокрЛП(ПолныеИменаОбъектов, ",");
	КонецЕсли;
	
	Если Не ПустаяСтрока(ПолныеИменаРегистровНачало) И Не ПустаяСтрока(ПерваяТабличная) Тогда // Есть табличные объекты.
		// Установить табличную часть начала выборки на место, на котором она остановилась в последний раз.
		ПолныеИменаРегистровНачалоМассив = ОстатокМассива(ПолныеИменаРегистровНачало, ПерваяТабличная);
	Иначе
		ПолныеИменаРегистровНачалоМассив = СтрРазделитьСокрЛП(ПолныеИменаРегистровНачало, ",");
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("СсылочныеОбъекты", ПолныеИменаОбъектовМассив);
	Результат.Вставить("ТабличныеОбъектыВсе", СтрРазделитьСокрЛП(ПолныеИменаРегистров, ","));
	Результат.Вставить("ТабличныеОбъектыНачало", ПолныеИменаРегистровНачалоМассив);
	Результат.Вставить("СсылочныйИндекс", 0);
	Результат.Вставить("ТабличныйИндекс", 0);
	
	Возврат Результат;
	
КонецФункции

// Получить следующую порцию параметров обхода данных для обновления.
//
// Параметры:
//  ПараметрыОбхода - см. ПараметрыОбходаДанныхДляОбновления
//
// Возвращаемое значение:
//   Структура - параметры обхода текущей итерации в виде структуры со следующими полями:
//    * СсылочныйОбъект - Строка - имя ссылочного объекта.
//    * ТабличныйОбъект - Строка - имя табличного объекта.
//   Неопределено - если обход закончен.
//
Функция ТекущиеПараметрыОбхода(ПараметрыОбхода)
	
	Если ПараметрыОбхода.СсылочныйИндекс < ПараметрыОбхода.СсылочныеОбъекты.Количество() Тогда
		Если ПараметрыОбхода.СсылочныйИндекс = 0 Тогда
			ТабличныеОбъекты = ПараметрыОбхода.ТабличныеОбъектыНачало;
		Иначе
			ТабличныеОбъекты = ПараметрыОбхода.ТабличныеОбъектыВсе;
		КонецЕсли;
		
		Если ПараметрыОбхода.ТабличныйИндекс < ТабличныеОбъекты.Количество() Тогда
			СсылочныйОбъект = ПараметрыОбхода.СсылочныеОбъекты[ПараметрыОбхода.СсылочныйИндекс];
			ТабличныйОбъект = ТабличныеОбъекты[ПараметрыОбхода.ТабличныйИндекс];
			
			Результат = Новый Структура;
			Результат.Вставить("СсылочныйОбъект", СсылочныйОбъект);
			Результат.Вставить("ТабличныйОбъект", ТабличныйОбъект);
			Результат.Вставить("СсылочныйИндекс", ПараметрыОбхода.СсылочныйИндекс);
			Результат.Вставить("ТабличныйИндекс", ПараметрыОбхода.ТабличныйИндекс);
			
			Возврат Результат;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Перейти к следующим параметрам выборки, если не было выбрано ни одной записи с текущими параметрами.
//
// Параметры:
//  ПараметрыОбхода - см. ПараметрыОбходаДанныхДляОбновления
//  ПолнаяВыборка - Булево - Истина, если выбрано максимум записей.
//  
Процедура СледующиеПараметрыОбхода(ПараметрыОбхода, ПолнаяВыборка)
	
	Если Не ПолнаяВыборка Тогда
		Если ПараметрыОбхода.СсылочныйИндекс = 0 Тогда
			ТабличныеОбъекты = ПараметрыОбхода.ТабличныеОбъектыНачало;
		Иначе
			ТабличныеОбъекты = ПараметрыОбхода.ТабличныеОбъектыВсе;
		КонецЕсли;
		
		Если ПараметрыОбхода.ТабличныйИндекс = ТабличныеОбъекты.ВГраница() Тогда
			ПараметрыОбхода.ТабличныйИндекс = 0;
			ПараметрыОбхода.СсылочныйИндекс = ПараметрыОбхода.СсылочныйИндекс + 1;
		Иначе
			ПараметрыОбхода.ТабличныйИндекс = ПараметрыОбхода.ТабличныйИндекс + 1;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Получить поля упорядочивания для заданных параметров поиска порции.
//
// Параметры:
//  ПараметрыПоиска - см. НовыеПараметрыПоискаПорции
//
// Возвращаемое значение:
//  Строка - Массив - поля упорядочивания.
//
Функция ПоляУпорядочиванияПриПоискеПорций(ПараметрыПоиска)
	
	ПараметрыВыборки = ПараметрыПоиска.ПараметрыВыборки;
	Возврат ?(ПараметрыПоиска.ФорсироватьОбновление,
		ПараметрыВыборки.ПоляУпорядочиванияПриОбработкеДанных,
		ПараметрыВыборки.ПоляУпорядочиванияПриРаботеПользователей);
	
КонецФункции

// Определить, есть ли упорядочивание по полям присоединяемых таблиц.
//
// Параметры:
//  ПоляУпорядочивания - Массив - поля упорядочивания.
//
// Возвращаемое значение:
//  Булево - Истина, если есть упорядочивание по полям присоединяемых таблиц.
//
Функция ЕстьУпорядочиваниеПоВнешнимТаблицам(ПоляУпорядочивания)
	
	Для каждого ПолеУпорядочивания Из ПоляУпорядочивания Цикл
		Если СтрНайти(ПолеУпорядочивания, ".") > 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Получить результат поиска, разбить его на порции и запустить потоки обновления.
//
// Параметры:
//  ПараметрыПоиска - см. НовыеПараметрыПоискаПорции
//  АдресРезультата - Строка - адрес результата выполнения НайтиПорциюДляОбновления
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//
Процедура ЗавершитьПоискПорцииДляОбновления(ПараметрыПоиска, АдресРезультата) Экспорт
	
	РезультатПоиска = ПолучитьИзВременногоХранилища(АдресРезультата);
	Группы = НовоеОписаниеГруппПотоковОбработчиковОтложенногоОбновления();
	КонтекстОбработчика = ПараметрыПоиска.КонтекстОбработчика;
	
	ОбработчикОбновления = ОбработчикОбновления(КонтекстОбработчика.ИмяОбработчика);
	ОписаниеПроцедурыЗаполнения = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
	ПорцииДляОбновления = ОписаниеПроцедурыЗаполнения.ПорцииДляОбновления; // см. НоваяТаблицаПорцийДляОбновления
	ИдентификаторПорции = ПараметрыПоиска.ИдентификаторПорции;
	ЕстьИдентификатор = ИдентификаторПорции <> Неопределено;
	СтараяПорция = ?(ЕстьИдентификатор, ПорцииДляОбновления.Найти(ИдентификаторПорции, "Идентификатор"), Неопределено);
	ЭтоПервыйПоиск = ПараметрыПоиска.ПоследняяВыбраннаяЗапись = Неопределено
	               И ПараметрыПоиска.ПерваяЗапись = Неопределено
	               И ПараметрыПоиска.ПоследняяЗапись = Неопределено;
	ЭтоПовторныйПоиск = ПараметрыПоиска.ПерваяЗапись <> Неопределено
	                  И ПараметрыПоиска.ПоследняяЗапись <> Неопределено;
	
	Если ЭтоПервыйПоиск Тогда
		СохранитьРезультатПервогоПоиска(РезультатПоиска, ОписаниеПроцедурыЗаполнения);
		ПорцииДляОбновления = ОписаниеПроцедурыЗаполнения.ПорцииДляОбновления;
	ИначеЕсли ЭтоПовторныйПоиск Тогда
		СохранитьРезультатПовторногоПоиска(РезультатПоиска, ОписаниеПроцедурыЗаполнения, ИдентификаторПорции);
	Иначе
		СохранитьРезультатПоиска(РезультатПоиска, ОписаниеПроцедурыЗаполнения);
	КонецЕсли;
	
	Если РезультатПоиска.Количество > 0 Тогда
		МаксимумПотоков = КоличествоПотоковОбновленияИнформационнойБазы();
		ПотокиОбновления = ПотокиОбновления();
		СвободныхПотоков = МаксимумПотоков - ПотокиОбновления.Количество() + 1;
		Если СвободныхПотоков = 0 Тогда
			СвободныхПотоков = 1;
		КонецЕсли;
		Фрагменты = РазбитьРезультатПоискаНаФрагменты(РезультатПоиска, СвободныхПотоков);
		КоличествоФрагментов = Фрагменты.Количество();
		
		Для НомерФрагмента = 0 По КоличествоФрагментов - 1 Цикл
			Фрагмент = Фрагменты[НомерФрагмента];
			ЕстьСтараяПорция = (НомерФрагмента = 0 И СтараяПорция <> Неопределено);
			
			Если ЕстьСтараяПорция Тогда
				Порция = СтараяПорция;
				Фрагмент.Идентификатор = Порция.Идентификатор;
			Иначе
				Порция = ПорцииДляОбновления.Добавить();
				Порция.Идентификатор = Фрагмент.Идентификатор;
			КонецЕсли;
			
			Порция.ПерваяЗапись = Фрагмент.ПерваяЗапись;
			Порция.ПоследняяЗапись = Фрагмент.ПоследняяЗапись;
			Порция.Обрабатывается = Истина;
			
			// Если порций больше одной, то для каждой свой КлючЗаписиПрогрессаОбновления прогресса обновления
			ПараметрыОбработчикаОбновления = Новый Структура(КонтекстОбработчика.ПараметрыОбработчикаОбновления);
			ПараметрыОбработчикаОбновления.КлючЗаписиПрогрессаОбновления = Новый УникальныйИдентификатор;
			Если РезультатПоиска.Свойство("ОбработаныАктуальныеДанные")
				И РезультатПоиска.ОбработаныАктуальныеДанные = Истина Тогда
				ПараметрыОбработчикаОбновления.ОбработаныАктуальныеДанные = Истина;
			КонецЕсли;
			Если РезультатПоиска.Свойство("ОбработанныеТаблицыРегистраторов")
				И РезультатПоиска.ОбработанныеТаблицыРегистраторов <> Неопределено Тогда
				ПараметрыОбработчикаОбновления.ОбработанныеТаблицыРегистраторов = РезультатПоиска.ОбработанныеТаблицыРегистраторов;
			КонецЕсли;
			КонтекстОбработчика.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(ПараметрыОбработчикаОбновления);
			ОбработатьФрагментДанныхВПотоке(Фрагмент, Группы, КонтекстОбработчика);
		КонецЦикла;
	Иначе
		Фрагмент = НоваяПорцияДляОбновления();
		Фрагмент.НаборДанных = НовыйНаборДанныхДляОбновления();
		ПараметрыОбработчикаОбновления = Новый Структура(КонтекстОбработчика.ПараметрыОбработчикаОбновления);
		Если РезультатПоиска.Свойство("ОбработаныАктуальныеДанные")
			И РезультатПоиска.ОбработаныАктуальныеДанные = Истина Тогда
			ПараметрыОбработчикаОбновления.ОбработаныАктуальныеДанные = Истина;
		КонецЕсли;
		
		Если РезультатПоиска.Свойство("ОбработанныеТаблицыРегистраторов")
			И РезультатПоиска.ОбработанныеТаблицыРегистраторов <> Неопределено Тогда
			ПараметрыОбработчикаОбновления.ОбработанныеТаблицыРегистраторов = РезультатПоиска.ОбработанныеТаблицыРегистраторов;
		КонецЕсли;
		КонтекстОбработчика.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(ПараметрыОбработчикаОбновления);
		
		ОбработатьФрагментДанныхВПотоке(Фрагмент, Группы, КонтекстОбработчика);
	КонецЕсли;
	
	ОписаниеПроцедурыЗаполнения.ВыполняетсяПоискПорции = Ложь;
	УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика,
		"ОбрабатываемыеДанные",
		Новый ХранилищеЗначения(ОписаниеПроцедурыЗаполнения));
	
КонецПроцедуры

// Обработчик аварийного завершения выполнения потока поиска порции для обновления.
//
// Параметры:
//  Поток - см. НовоеОписаниеПотоков
//  ИнформацияОбОшибке - ИнформацияОбОшибке - описание возникшей ошибки.
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы.
//
Процедура ПриАварийномЗавершенииПотокаПоискаПорцииДляОбновления(Поток, ИнформацияОбОшибке) Экспорт
	
	ОбработчикОбновления = ОбработчикОбновления(Поток.ПараметрыПроцедуры.ИмяОбработчика);
	Описание = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
	Описание.ВыполняетсяПоискПорции = Ложь;
	УстановитьСвойствоОбработчика(Поток.ПараметрыПроцедуры.ИмяОбработчика,
		"ОбрабатываемыеДанные",
		Новый ХранилищеЗначения(Описание));
	
КонецПроцедуры

// Обработчик отмены выполнения потока поиска порции для обновления.
//
// Параметры:
//  Поток - см. НовоеОписаниеПотоков
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//
Процедура ПриОтменеПотокаПоискаПорцииДляОбновления(Поток) Экспорт
	
	ОбработчикОбновления = ОбработчикОбновления(Поток.ПараметрыПроцедуры.ИмяОбработчика);
	Описание = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
	Описание.ВыполняетсяПоискПорции = Ложь;
	УстановитьСвойствоОбработчика(Поток.ПараметрыПроцедуры.ИмяОбработчика,
		"ОбрабатываемыеДанные",
		Новый ХранилищеЗначения(Описание));
	
КонецПроцедуры

// Завершить обработку данных многопоточного обработчика обновления.
// Удалить обработанную порцию данных.
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//
Процедура ЗавершитьВыполнениеМногопоточногоОбработчика(КонтекстОбработчика, ИмяОбработчика)
	
	ОбработчикОбновления = ОбработчикОбновления(ИмяОбработчика);
	
	ОписаниеПроцедурыЗаполнения = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
	ПорцииДляОбновления = ОписаниеПроцедурыЗаполнения.ПорцииДляОбновления;
	
	Если ПорцииДляОбновления <> Неопределено
		И КонтекстОбработчика.Параметры.Свойство("ОбновляемыеДанные") Тогда
		ОбновляемыеДанные = КонтекстОбработчика.Параметры.ОбновляемыеДанные;
		
		Если ОбновляемыеДанные <> Неопределено Тогда
			Порция = ПорцииДляОбновления.Найти(ОбновляемыеДанные.Идентификатор, "Идентификатор");
			
			Если Порция <> Неопределено Тогда
				ПорцииДляОбновления.Удалить(Порция);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика,
		"ОбрабатываемыеДанные",
		Новый ХранилищеЗначения(ОписаниеПроцедурыЗаполнения));
	
КонецПроцедуры

// Отменить обработку данных многопоточного обработчика обновления.
// Отметить, что найденную порцию данных нужно будет обрабатывать повторно.
//
// Параметры:
//  Поток - СтрокаТаблицыЗначений:
//     * Наименование - Строка - произвольное наименование потока (используется в наименовании фонового задания).
//     * Группа - Строка - имя группы с описанием потока.
//     * ИдентификаторЗадания - УникальныйИдентификатор - уникальный идентификатор фонового задания.
//     * ПараметрыПроцедуры - см. НовыйКонтекстОбработчика
//     * ПараметрыПроцедурыЗавершения - Произвольный - параметры для ПроцедураЗавершения.
//     * АдресРезультата - Строка - адрес временного хранилища для сохранения результата выполнения фонового задания.
//  ОбработчикОбновления - СтрокаДереваЗначений - обработчик обновления в виде строки дерева обработчиков.
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//  
Процедура ОтменитьОбработкуДанныхМногопоточногоОбработчика(Поток, ОбработчикОбновления)
	
	ОписаниеПроцедурыЗаполнения = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
	ПорцииДляОбновления = ОписаниеПроцедурыЗаполнения.ПорцииДляОбновления;
	
	Если ПорцииДляОбновления <> Неопределено Тогда
		ОбновляемыеДанные = Поток.ПараметрыПроцедуры.Параметры.ОбновляемыеДанные;
		
		Если ОбновляемыеДанные <> Неопределено Тогда
			Порция = ПорцииДляОбновления.Найти(ОбновляемыеДанные.Идентификатор, "Идентификатор");
			
			Если Порция <> Неопределено Тогда
				Порция.Обрабатывается = Ложь;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика,
		"ОбрабатываемыеДанные",
		Новый ХранилищеЗначения(ОписаниеПроцедурыЗаполнения));
	
КонецПроцедуры

// Сохранить результат первого поиска данных для многопоточного обработчика.
//
// Параметры:
//  РезультатПоиска - см. НовыйРезультатПоискаПорции
//  ОписаниеПроцедурыЗаполнения - см. НовоеОписаниеОбрабатываемыхДанных
//
Процедура СохранитьРезультатПервогоПоиска(РезультатПоиска, ОписаниеПроцедурыЗаполнения)
	
	Если РезультатПоиска.Количество > 0 Тогда
		ПоследняяВыбраннаяЗапись = КлючЗаписиПоследнейСтрокиНабораДанных(РезультатПоиска.НаборДанных);
		ОписаниеПроцедурыЗаполнения.ПоследняяВыбраннаяЗапись = ПоследняяВыбраннаяЗапись;
		
		Если ОписаниеПроцедурыЗаполнения.ПорцииДляОбновления = Неопределено Тогда
			ОписаниеПроцедурыЗаполнения.ПорцииДляОбновления = НоваяТаблицаПорцийДляОбновления();
		КонецЕсли;
	Иначе
		ОписаниеПроцедурыЗаполнения.ПоследняяВыбраннаяЗапись = Неопределено;
	КонецЕсли;
	
	ОписаниеПроцедурыЗаполнения.ПоискЗавершен = РезультатПоиска.ПоискЗавершен;
	
КонецПроцедуры

// Сохранить результат повторного (после ошибки) поиска данных для многопоточного обработчика.
//
// Параметры:
//  РезультатПоиска - см. НовыйРезультатПоискаПорции
//  ОписаниеПроцедурыЗаполнения - см. НовоеОписаниеОбрабатываемыхДанных
//  ИдентификаторПорции - УникальныйИдентификатор - идентификатор порции для которой искались данные.
//
Процедура СохранитьРезультатПовторногоПоиска(РезультатПоиска, ОписаниеПроцедурыЗаполнения, ИдентификаторПорции)
	
	Если РезультатПоиска.Количество = 0 Тогда
		ПорцииДляОбновления = ОписаниеПроцедурыЗаполнения.ПорцииДляОбновления;
		Порция = ПорцииДляОбновления.Найти(ИдентификаторПорции, "Идентификатор");
		ПорцииДляОбновления.Удалить(Порция);
	КонецЕсли;
	
	ОписаниеПроцедурыЗаполнения.ПоискЗавершен = РезультатПоиска.ПоискЗавершен;
	
	Если ОписаниеПроцедурыЗаполнения.Свойство("ИмяОбработчика") Тогда
		УстановитьСвойствоОбработчика(ОписаниеПроцедурыЗаполнения.ИмяОбработчика,
			"ОбрабатываемыеДанные",
			Новый ХранилищеЗначения(ОписаниеПроцедурыЗаполнения));
	КонецЕсли;
	
КонецПроцедуры

// Сохранить результат поиска данных для многопоточного обработчика.
//
// Параметры:
//  РезультатПоиска - см. НовыйРезультатПоискаПорции
//  ОписаниеПроцедурыЗаполнения - см. НовоеОписаниеОбрабатываемыхДанных
//
Процедура СохранитьРезультатПоиска(РезультатПоиска, ОписаниеПроцедурыЗаполнения)
	
	Если РезультатПоиска.Количество > 0 Тогда
		ПоследняяВыбраннаяЗапись = КлючЗаписиПоследнейСтрокиНабораДанных(РезультатПоиска.НаборДанных);
		ОписаниеПроцедурыЗаполнения.ПоследняяВыбраннаяЗапись = ПоследняяВыбраннаяЗапись;
	КонецЕсли;
	
	ОписаниеПроцедурыЗаполнения.ПоискЗавершен = РезультатПоиска.ПоискЗавершен;
	
КонецПроцедуры

// Разбить найденные данные на указанное количество порций.
//
// Параметры:
//  РезультатПоиска - см. НовыйРезультатПоискаПорции
//  КоличествоФрагментов - Число - количество фрагментов на которые нужно разбить данные.
//
// Возвращаемое значение:
//   см. НовыйНаборПорцийДляОбновления
//
Функция РазбитьРезультатПоискаНаФрагменты(РезультатПоиска, Знач КоличествоФрагментов)
	
	Фрагменты = НовыйНаборПорцийДляОбновления();
	НайденныйНаборДанных = РезультатПоиска.НаборДанных;
	КоличествоНайденных = РезультатПоиска.Количество;
	КоличествоФрагментов = ?(КоличествоНайденных < КоличествоФрагментов, 1, КоличествоФрагментов);
	МаксимальныйРазмерПорции = Цел(КоличествоНайденных / КоличествоФрагментов);
	КоличествоОбработанных = 0;
	
	Для НомерФрагмента = 1 По КоличествоФрагментов Цикл // Разбивка идет с конца найденного набора данных.
		Фрагмент = НоваяПорцияДляОбновления();
		Фрагмент.Идентификатор = Новый УникальныйИдентификатор;
		Фрагмент.НаборДанных = НовыйНаборДанныхДляОбновления();
		Фрагмент.ПоследняяЗапись = КлючЗаписиПоследнейСтрокиНабораДанных(НайденныйНаборДанных);
		Фрагменты.Вставить(0, Фрагмент);
		ИндексНабораДанных = НайденныйНаборДанных.Количество() - 1;
		КоличествоСвободных = ?(НомерФрагмента = КоличествоФрагментов,
			КоличествоНайденных - КоличествоОбработанных,
			МаксимальныйРазмерПорции);
		
		Пока ИндексНабораДанных >= 0 Цикл
			ТекущаяСтрокаДанных = НайденныйНаборДанных[ИндексНабораДанных];
			ТекущиеДанные = ТекущаяСтрокаДанных.Данные;
			ТекущееКоличество = ТекущиеДанные.Количество();
			ДанныеФрагмента = Фрагмент.НаборДанных.Добавить();
			
			Если ТекущееКоличество <= КоличествоСвободных Тогда
				ЗаполнитьЗначенияСвойств(ДанныеФрагмента, ТекущаяСтрокаДанных);
				НайденныйНаборДанных.Удалить(ИндексНабораДанных);
				КоличествоСвободных = КоличествоСвободных - ТекущееКоличество;
				КоличествоОбработанных = КоличествоОбработанных + ТекущееКоличество;
			Иначе
				ЗаполнитьЗначенияСвойств(ДанныеФрагмента, ТекущаяСтрокаДанных, "СсылочныйОбъект, ТабличныйОбъект");
				НачалоВырезки = ТекущееКоличество - КоличествоСвободных;
				ДанныеФрагмента.Данные = ВырезатьСтрокиИзТаблицыЗначений(ТекущиеДанные, НачалоВырезки, КоличествоСвободных);
				КоличествоОбработанных = КоличествоОбработанных + КоличествоСвободных;
				КоличествоСвободных = 0;
			КонецЕсли;
			
			Если КоличествоСвободных = 0 Тогда
				Прервать;
			Иначе
				ИндексНабораДанных = ИндексНабораДанных - 1;
			КонецЕсли;
		КонецЦикла;
		
		Фрагмент.ПерваяЗапись = КлючЗаписиПоследнейСтрокиНабораДанных(Фрагмент.НаборДанных);
	КонецЦикла;
	
	Возврат Фрагменты;
	
КонецФункции

// Вырезать СуществующиеПоставляемыеЭлементы таблицы значений в новую таблицу значений.
//
// Параметры:
//  Таблица - ТаблицаЗначений - таблица из которой вырезаются строки.
//  Начало - Число - индекс первой вырезаемой строки.
//  Количество - Число - количество вырезаемых строк.
//
// Возвращаемое значение:
//  ТаблицаЗначений - вырезанные строки в виде новой таблицы значений.
//
Функция ВырезатьСтрокиИзТаблицыЗначений(Таблица, Начало, Количество)
	
	НоваяТаблица = Таблица.СкопироватьКолонки();
	Индекс = Начало + Количество - 1;
	
	Пока Индекс >= Начало Цикл
		НоваяСтрока = НоваяТаблица.Добавить();
		СтараяСтрока = Таблица[Индекс];
		ЗаполнитьЗначенияСвойств(НоваяСтрока, СтараяСтрока);
		Таблица.Удалить(СтараяСтрока);
		Индекс = Индекс - 1;
	КонецЦикла;
	
	Возврат НоваяТаблица;
	
КонецФункции

// Определяет, есть ли в обработчике порции, которые можно было бы обновить в новом потоке.
//
// Параметры:
//  ОбработчикОбновления - СтрокаТаблицыЗначений - обработчик обновления в виде строки дерева обработчиков.
//
Функция ЕстьПорцииДляОбновления(ОбработчикОбновления)
	
	Описание = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
	
	Если Описание.ВыполняетсяПоискПорции Тогда
		Возврат Ложь;
	Иначе
		Если Описание.ПорцииДляОбновления <> Неопределено И Описание.ПорцииДляОбновления.Количество() > 0 Тогда
			Для каждого Порция Из Описание.ПорцииДляОбновления Цикл
				Если Не Порция.Обрабатывается Тогда
					Возврат Истина;
				КонецЕсли;
			КонецЦикла;
			
			Возврат Ложь;
		Иначе
			Возврат Истина;
		КонецЕсли;
	КонецЕсли;
	
КонецФункции

// Получить ключ записи первой строки набора данных.
//
// Параметры:
//  НаборДанных - см. НовыйНаборДанныхДляОбновления
//
// Возвращаемое значение:
//  СписокЗначений - ключ записи.
//
Функция КлючЗаписиПервойСтрокиНабораДанных(НаборДанных)
	
	ПерваяСтрокаДанных = НаборДанных[0];
	Возврат НовыйКлючЗаписиИзТаблицыДанных(ПерваяСтрокаДанных.СсылочныйОбъект,
		ПерваяСтрокаДанных.ТабличныйОбъект,
		ПерваяСтрокаДанных.Данные,
		0);
	
КонецФункции

// Получить ключ записи последней строки набора данных.
//
// Параметры:
//  НаборДанных - см. НовыйНаборДанныхДляОбновления
//
// Возвращаемое значение:
//  СписокЗначений - ключ записи.
//
Функция КлючЗаписиПоследнейСтрокиНабораДанных(НаборДанных)
	
	ПоследняяСтрокаДанных = НаборДанных[НаборДанных.Количество() - 1];
	Возврат НовыйКлючЗаписиИзТаблицыДанных(ПоследняяСтрокаДанных.СсылочныйОбъект,
		ПоследняяСтрокаДанных.ТабличныйОбъект,
		ПоследняяСтрокаДанных.Данные,
		ПоследняяСтрокаДанных.Данные.Количество() - 1);
	
КонецФункции

// Таблица с описанием порций обновляемых данных.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Идентификатор - УникальныйИдентификатор - идентификатор порции.
//   * ПерваяЗапись - СписокЗначений - первая запись в порции, где:
//     ** Представление - Строка - имя поля.
//     ** Значение - Произвольный - значение поля ключа.
//   * ПоследняяЗапись - СписокЗначений - последняя запись в порции, где:
//     ** Представление - Строка - имя поля.
//     ** Значение - Произвольный - значение поля ключа.
//   * Обрабатывается - Булево - Истина, если запущен поток обновления данных.
//
Функция НоваяТаблицаПорцийДляОбновления()
	
	Таблица = Новый ТаблицаЗначений;
	Колонки = Таблица.Колонки;
	Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("УникальныйИдентификатор"));
	Колонки.Добавить("ПерваяЗапись", Новый ОписаниеТипов("СписокЗначений"));
	Колонки.Добавить("ПоследняяЗапись", Новый ОписаниеТипов("СписокЗначений"));
	Колонки.Добавить("Обрабатывается", Новый ОписаниеТипов("Булево"));
	Таблица.Индексы.Добавить("Идентификатор");
	
	Возврат Таблица;
	
КонецФункции

// Описание обрабатываемых данных обработчика обновления (для СведенияОбОбновлении.ОбрабатываемыеДанные).
//
// Параметры:
//  Многопоточное - Булево - Истина, если используется для многопоточного обработчика обновления.
//  Фоновое - Булево - Истина, если используется для ЗаполнитьДанныеОтложенногоОбработчика().
//
// Возвращаемое значение:
//  Структура - описание со следующими полями:
//   * ДанныеОбработчика - Соответствие - данные регистрируемые и обрабатываемые обработчиком обновления.
//   * ВыполняетсяПоискПорции - Булево - признак наличия потока, выполняющего поиск порции данных для обновления.
//   * ПараметрыВыборки - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки
//   * ПорцииДляОбновления - см. НоваяТаблицаПорцийДляОбновления
//   * ПоследняяВыбраннаяЗапись - СписокЗначений - описание начала отбора при страничной выборке:
//     ** Представление - Строка - имя поля.
//     ** Значение - Произвольный - значение поля.
//   * ПоискЗавершен - Булево - Истина, поиск больше выполняться не будет (завершен).
//   * ОбработкаЗавершена - Булево - признак завершения обработки, заполняемый обработчиком обновления.
//   * ИмяОбработчика - Строка - имя обработчика обновления.
//   * Очередь - Число - номер очереди обработчика обновления.
//   * ПроцедураЗаполнения - Строка - имя процедуры заполнения данных для обновления.
//   * Статус - Строка - статус обработки данных.
//
Функция НовоеОписаниеОбрабатываемыхДанных(Многопоточное = Ложь, Фоновое = Ложь) Экспорт
	
	Описание = Новый Структура;
	Описание.Вставить("ДанныеОбработчика");
	Описание.Вставить("ИмяОбработчика");
	Описание.Вставить("АктуальныеДанные");
	Описание.Вставить("ЗарегистрированныеТаблицыРегистраторов");
	Описание.Вставить("ОбработанныеТаблицыРегистраторов");
	Описание.Вставить("ВерсияПодсистемыНаНачалоОбновления");
	
	Если Многопоточное Тогда
		Описание.Вставить("ВыполняетсяПоискПорции", Ложь);
		Описание.Вставить("ПараметрыВыборки");
		Описание.Вставить("ПорцииДляОбновления");
		Описание.Вставить("ПоследняяВыбраннаяЗапись");
		Описание.Вставить("ПоискЗавершен", Ложь);
	КонецЕсли;
	
	Если Фоновое Тогда
		Описание.Вставить("Очередь");
		Описание.Вставить("ПроцедураЗаполнения");
		Описание.Вставить("Статус");
	КонецЕсли;
	
	Возврат Описание;
	
КонецФункции

// Отбор для процедуры НайтиПорциюДляОбновления().
// Если заполнена ПоследняяВыбраннаяЗапись, то выполняется поиск первых 10000 записей после нее.
// Иначе выполняется поиск записей между ПерваяЗапись и ПоследняяЗапись.
//
// Возвращаемое значение:
//  Структура - отбор со следующими полями:
//   * ИдентификаторПорции - УникальныйИдентификатор - идентификатор порции для которой ищутся данные.
//   * КонтекстОбработчика - см. НовыйКонтекстОбработчика
//   * ПоследняяВыбраннаяЗапись - СписокЗначений - описание начала отбора при страничной выборке:
//     ** Представление - Строка - имя поля.
//     ** Значение - Произвольный - значение поля.
//   * ПерваяЗапись - СписокЗначений - первая запись в порции, где:
//     ** Представление - Строка - имя поля.
//     ** Значение - Произвольный - значение поля ключа.
//   * ПоследняяЗапись - СписокЗначений - последняя запись в порции, где:
//     ** Представление - Строка - имя поля.
//     ** Значение - Произвольный - значение поля ключа.
//   * ПараметрыВыборки - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки
//   * Очередь - Число - номер очереди обработчика.
//
Функция НовыеПараметрыПоискаПорции()
	
	ПараметрыПоиска = Новый Структура;
	ПараметрыПоиска.Вставить("ИдентификаторПорции");
	ПараметрыПоиска.Вставить("ИмяОбработчика");
	ПараметрыПоиска.Вставить("КонтекстОбработчика");
	ПараметрыПоиска.Вставить("ПоследняяВыбраннаяЗапись");
	ПараметрыПоиска.Вставить("ПерваяЗапись");
	ПараметрыПоиска.Вставить("ПоследняяЗапись");
	ПараметрыПоиска.Вставить("ПараметрыВыборки");
	ПараметрыПоиска.Вставить("Очередь");
	ПараметрыПоиска.Вставить("ФорсироватьОбновление", Ложь);
	
	Возврат ПараметрыПоиска;
	
КонецФункции

// Ключ записи порции данных.
//
// Параметры:
//  СсылочныйОбъект - Строка - полное имя объекта метаданных ссылочного типа.
//  ТабличныйОбъект - Строка - полное имя объекта метаданных табличного типа.
//
// Возвращаемое значение:
//  СписокЗначений из Строка
//
Функция НовыйКлючЗаписи(СсылочныйОбъект, ТабличныйОбъект)
	
	КлючЗаписи = Новый СписокЗначений;
	КлючЗаписи.Добавить(СсылочныйОбъект);
	КлючЗаписи.Добавить(ТабличныйОбъект);
	
	Возврат КлючЗаписи;
	
КонецФункции

// Ключ записи порции из таблицы с данными.
//
// Параметры:
//  СсылочныйОбъект - Строка - полное имя объекта метаданных ссылочного типа.
//  ТабличныйОбъект - Строка - полное имя объекта метаданных табличного типа.
//  Данные - ТаблицаЗначений - данные порции.
//  Индекс - Число - индекс строки данных для формирования ключа.
//
// Возвращаемое значение:
//  СписокЗначений - ключ записи.
//
Функция НовыйКлючЗаписиИзТаблицыДанных(СсылочныйОбъект, ТабличныйОбъект, Данные, Индекс)
	
	КлючЗаписи = НовыйКлючЗаписи(СсылочныйОбъект, ТабличныйОбъект);
	Строка = Данные[Индекс];
	
	Для каждого Колонка Из Данные.Колонки Цикл
		ИмяКолонки = Колонка.Имя;
		КлючЗаписи.Добавить(Строка[ИмяКолонки], ИмяКолонки);
	КонецЦикла;
	
	Возврат КлючЗаписи;
	
КонецФункции

// Таблица значений с описанием данных для обновления.
// Является результатом поиска данных для обновления.
//
// Возвращаемое значение:
//  ТаблицаЗначений - описание порций со следующей структурой:
//   * СсылочныйОбъект - Строка - имя ссылочного объекта метаданных (может быть Неопределено).
//   * ТабличныйОбъект - Строка - имя табличного объекта метаданных (может быть Неопределено).
//   * Данные - ТаблицаЗначений - выборка из СУБД в виде таблицы значений.
//
Функция НовыйНаборДанныхДляОбновления()
	
	НаборДанных = Новый ТаблицаЗначений;
	Колонки = НаборДанных.Колонки;
	Колонки.Добавить("СсылочныйОбъект", Новый ОписаниеТипов("Строка"));
	Колонки.Добавить("ТабличныйОбъект", Новый ОписаниеТипов("Строка"));
	Колонки.Добавить("Данные", Новый ОписаниеТипов("ТаблицаЗначений"));
	
	Возврат НаборДанных;
	
КонецФункции

// Массив описаний порций данных для обновления.
// Является результатом разбиения найденных данных на фрагменты.
//
// Возвращаемое значение:
//   Массив из см. НоваяПорцияДляОбновления
//
Функция НовыйНаборПорцийДляОбновления()
	
	Возврат Новый Массив;
	
КонецФункции

// Описание порции данных для обновления.
//
// Возвращаемое значение:
//  Структура - описание порции со следующей структурой:
//   * Идентификатор - УникальныйИдентификатор - идентификатор порции.
//   * ПерваяЗапись - СписокЗначений - ключ первой записи порции, см. НовыйКлючЗаписиИзДанныхПорции.
//   * ПоследняяЗапись - СписокЗначений - ключ последней записи порции, см. НовыйКлючЗаписиИзДанныхПорции.
//   * НаборДанных - см. НовыйНаборДанныхДляОбновления
//
Функция НоваяПорцияДляОбновления()
	
	Порция = Новый Структура;
	Порция.Вставить("Идентификатор");
	Порция.Вставить("ПерваяЗапись");
	Порция.Вставить("ПоследняяЗапись");
	Порция.Вставить("НаборДанных");
	
	Возврат Порция;
	
КонецФункции

// Результат выполнения поиска порции.
//
// Возвращаемое значение:
//  Структура - результат поиска со следующими полями:
//   Количество - Число - количество выбранных записей.
//   НаборДанных - см. НовыйНаборДанныхДляОбновления
//   ПоискЗавершен - Булево - Истина, если искать больше нечего.
//
Функция НовыйРезультатПоискаПорции()
	
	РезультатПоиска = Новый Структура;
	РезультатПоиска.Вставить("Количество", 0);
	РезультатПоиска.Вставить("НаборДанных");
	РезультатПоиска.Вставить("ПоискЗавершен", Ложь);
	РезультатПоиска.Вставить("ОбработаныАктуальныеДанные", Ложь);
	РезультатПоиска.Вставить("ОбработанныеТаблицыРегистраторов", Неопределено);
	
	Возврат РезультатПоиска;
	
КонецФункции

// Найти первую не обработанную порцию (обработка которой завершилась аварийно).
//
// Параметры:
//  ПорцииДляОбновления - см. НоваяТаблицаПорцийДляОбновления
//
// Возвращаемое значение:
//  СтрокаТаблицыЗначений из см. НоваяТаблицаПорцийДляОбновления
//  Неопределено - если необработанных порций нет.
//
Функция ПерваяНеобработаннаяПорция(ПорцииДляОбновления)
	
	Если ПорцииДляОбновления <> Неопределено Тогда
		Для каждого Порция Из ПорцииДляОбновления Цикл
			Если Не Порция.Обрабатывается Тогда
				Возврат Порция;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Аналог СтрРазделить, дополненный тем, что фрагменты сокращены слева и справа.
//
// Параметры:
//  Строка - Строка - разделяемая строка.
//  Разделитель - Строка - разделитель элементов строки.
//  ВключатьПустые - Булево - Истина, если в результат попадают пустые строки.
//
// Возвращаемое значение:
//  Массив - элементы строки, разбитые по разделителю.
//
Функция СтрРазделитьСокрЛП(Строка, Разделитель, ВключатьПустые = Истина)
	
	Массив = СтрРазделить(Строка, Разделитель, ВключатьПустые);
	
	Для Индекс = 0 По Массив.ВГраница() Цикл
		Массив[Индекс] = СокрЛП(Массив[Индекс]);
	КонецЦикла;
	
	Возврат Массив;
	
КонецФункции

// Определяет, является ли описание обработчика многопоточным.
//
// Возвращаемое значение:
//  Булево - Истина, если описание многопоточное.
//
Функция ЭтоОписаниеДанныхМногопоточногоОбработчика(Описание)
	
	Возврат Описание.Свойство("ПорцииДляОбновления");
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// НАЧАЛЬНОЕ ЗАПОЛНЕНИЕ ИНФОРМАЦИОННОЙ БАЗЫ

// Зарегистрировать предопределенные элементы для обновления в обработчике обновления.
//
Процедура ЗарегистрироватьПредопределенныеЭлементыДляОбновления(Параметры, ОбъектМетаданных = Неопределено, ДополнительныеПараметры = Неопределено) Экспорт
	
	ПараметрыРегистрации = ПараметрыРегистрацииПредопределенныхЭлементов();
	
	Если ТипЗнч(ДополнительныеПараметры) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(ПараметрыРегистрации, ДополнительныеПараметры);
	КонецЕсли;
	
	ОбъектыСПредопределеннымиЭлементами = ?(ОбъектМетаданных <> Неопределено,
		ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ОбъектМетаданных),
		ОбъектыСНачальнымЗаполнением());
	
	ОбъектыКОбработке = Новый Массив;
	
	ШаблонЗапроса = "ВЫБРАТЬ
		|	&Поля
		|ИЗ
		|	#Таблица КАК Таблица
		|ГДЕ
		|	Таблица.Предопределенный = ИСТИНА";
	
	Для каждого ОбъектМетаданныхСЭлементами Из ОбъектыСПредопределеннымиЭлементами Цикл
		
		МенеджерОбъекта = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ОбъектМетаданныхСЭлементами.ПолноеИмя());
		ЛокализуемыеРеквизитыОбъекта = Новый Соответствие;
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
			МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
			ЛокализуемыеРеквизитыОбъекта = МодульМультиязычностьСервер.МультиязычныеРеквизитыОбъекта(ОбъектМетаданныхСЭлементами);
		КонецЕсли;
		
		ПредопределенныеДанные       = ПредопределенныеДанныеОбъекта(ОбъектМетаданныхСЭлементами, МенеджерОбъекта, ЛокализуемыеРеквизитыОбъекта);
		
		// Формируем запрос
		
		ИменаРеквизитовОбъекта = Новый Массив;
		Если ПараметрыРегистрации.РежимОбновления = "НовыеИИзмененные" Тогда
			
			Для Каждого Колонка Из ПредопределенныеДанные.Колонки Цикл
				Если ПустаяСтрока(Колонка.Заголовок) Тогда
					ИменаРеквизитовОбъекта.Добавить(Колонка.Имя);
				КонецЕсли;
			КонецЦикла;
			
		КонецЕсли;
		
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
			МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
			МодульМультиязычностьСервер.СформироватьИменаМультиязычныхРеквизитов(ИменаРеквизитовОбъекта, ЛокализуемыеРеквизитыОбъекта);
		КонецЕсли;
		
		Если ИменаРеквизитовОбъекта.Найти("ИмяПредопределенныхДанных") = Неопределено Тогда
			ИменаРеквизитовОбъекта.Добавить("ИмяПредопределенныхДанных");
		КонецЕсли;
		Если ИменаРеквизитовОбъекта.Найти("Ссылка") = Неопределено Тогда
			ИменаРеквизитовОбъекта.Добавить("Ссылка");
		КонецЕсли;
		
		ТекстЗапроса = СтрЗаменить(ШаблонЗапроса, "&Поля", СтрСоединить(ИменаРеквизитовОбъекта, ", " + Символы.ПС));
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#Таблица", ОбъектМетаданныхСЭлементами.ПолноеИмя());
		Запрос = Новый Запрос(ТекстЗапроса);
		
		// @skip-check query-in-loop - запросы к разным таблицам с уникальным составом реквизитов в каждой.
		РезультатЗапроса = Запрос.Выполнить();
		Если РезультатЗапроса.Пустой() Тогда
			Продолжить;
		КонецЕсли;
		
		ДанныеЗапроса = РезультатЗапроса.Выбрать();
		Пока ДанныеЗапроса.Следующий() Цикл
			
			ПоставляемыеСведения = ПредопределенныеДанные.Найти(ДанныеЗапроса.ИмяПредопределенныхДанных, "ИмяПредопределенныхДанных");
			
			Если ПоставляемыеСведения <> Неопределено Тогда
				
				Если ПараметрыРегистрации.РежимОбновления = "Все" Тогда
					ОбъектыКОбработке.Добавить(ДанныеЗапроса.Ссылка);
				ИначеЕсли ПараметрыРегистрации.РежимОбновления = "НовыеИИзмененные" Тогда
					
					Если ДанныеСодержатОтличия(ДанныеЗапроса, ИменаРеквизитовОбъекта, ПоставляемыеСведения, ЛокализуемыеРеквизитыОбъекта, ПараметрыРегистрации) Тогда
						ОбъектыКОбработке.Добавить(ДанныеЗапроса.Ссылка);
					КонецЕсли;
					
				ИначеЕсли ПараметрыРегистрации.РежимОбновления = "МультиязычныеСтроки" Тогда
					
					Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
						МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
						Если МодульМультиязычностьСервер.МультиязычныеСтрокиРеквизитовИзменены(
							ПоставляемыеСведения, ДанныеЗапроса, ЛокализуемыеРеквизитыОбъекта, ПараметрыРегистрации) Тогда
							ОбъектыКОбработке.Добавить(ДанныеЗапроса.Ссылка);
						КонецЕсли;
					КонецЕсли;
					
				КонецЕсли;
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЦикла;
	
	Если ОбъектыКОбработке.Количество() > 0 Тогда
		ОбновлениеИнформационнойБазы.ОтметитьКОбработке(Параметры, ОбъектыКОбработке);
	КонецЕсли;
	
КонецПроцедуры

// Заполнить объект начальными данными.
// 
// Параметры:
//  ЗаполняемыйОбъект - СправочникОбъект
//                    - ПланВидовХарактеристикОбъект
//                    - ДанныеФормыСтруктура:
//                       * Ссылка - СправочникСсылка
//  НастройкиЗаполнения - см. ОбновлениеИнформационнойБазы.НастройкиЗаполнения
// 
Процедура ЗаполнитьОбъектНачальнымиДанными(ЗаполняемыйОбъект, НастройкиЗаполнения) Экспорт
	
	Если ТипЗнч(ЗаполняемыйОбъект) = Тип("ДанныеФормыСтруктура") Тогда
		Если ЗаполняемыйОбъект.Свойство("ИсходныйКлючЗаписи") Тогда
			ТипОбъекта = ТипЗнч(ЗаполняемыйОбъект.ИсходныйКлючЗаписи);
		Иначе
			ТипОбъекта = ТипЗнч(ЗаполняемыйОбъект.Ссылка);
		КонецЕсли;
	Иначе
		ТипОбъекта = ТипЗнч(ЗаполняемыйОбъект);
	КонецЕсли;
	
	МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипОбъекта);
	
	НаборПараметровЗаполнения = НаборПараметровДляЗаполненияОбъекта(МетаданныеОбъекта);
	
	ЗаполнитьРеквизитыНачальнымиДанными(ЗаполняемыйОбъект, НаборПараметровЗаполнения, НастройкиЗаполнения);
	
КонецПроцедуры

Функция ИмяКлючевогоРеквизита(НаборДанныхДляЗаполнения)
	
	ИмяКлючевогоРеквизита = НаборДанныхДляЗаполнения.ПереопределяемыеНастройки.ИмяКлючевогоРеквизита;
	Если Не ЗначениеЗаполнено(ИмяКлючевогоРеквизита) Тогда
		ИмяКлючевогоРеквизита = "ИмяПредопределенныхДанных";
	КонецЕсли;
	
	Возврат ИмяКлючевогоРеквизита;
	
КонецФункции

Процедура ЗаполнитьЭлементыНачальнымиДанными(Параметры, ОбъектМетаданных, НастройкиЗаполнения) Экспорт
	
	СсылкиНаОбъекты = ОбновлениеИнформационнойБазы.ВыбратьСсылкиДляОбработки(Параметры.Очередь, ОбъектМетаданных.ПолноеИмя());
	
	Если НастройкиЗаполнения.ОбновитьТолькоМультиязычныеСтроки Тогда
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
			МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
			Результат = МодульМультиязычностьСервер.ОбновитьМультиязычныеСтрокиПредопределенныхЭлементов(СсылкиНаОбъекты, ОбъектМетаданных);
		КонецЕсли;
	Иначе
		Результат = ОбновитьЭлементыПредопределенныхЭлементов(СсылкиНаОбъекты, ОбъектМетаданных, НастройкиЗаполнения);
	КонецЕсли;
	
	Параметры.ОбработкаЗавершена = ОбновлениеИнформационнойБазы.ОбработкаДанныхЗавершена(Параметры.Очередь, ОбъектМетаданных.ПолноеИмя());
	
	Если Результат.ОбъектовОбработано = 0 И Результат.ПроблемныхОбъектов <> 0 Тогда
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось заполнить начальными данными некоторые элементы (пропущены): %1'"),
			Результат.ПроблемныхОбъектов);
		ВызватьИсключение ТекстСообщения;
	Иначе
		ЗаписьЖурналаРегистрации(ОбновлениеИнформационнойБазы.СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Информация,
			ОбъектМетаданных,, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Обработана очередная порция элементов: %1'"),
		Результат.ОбъектовОбработано));
	КонецЕсли;
	
КонецПроцедуры

Функция ПараметрыРегистрацииПредопределенныхЭлементов()
	
	ДополнительныеПараметры = Новый Структура;
	ДополнительныеПараметры.Вставить("РежимОбновления",         "НовыеИИзмененные");
	ДополнительныеПараметры.Вставить("ПропускатьПустые",         Истина);
	ДополнительныеПараметры.Вставить("СравниватьТабличныеЧасти", Ложь);
	
	Возврат ДополнительныеПараметры;
	
КонецФункции

Функция ДанныеСодержатОтличия(ДанныеЗапроса, ИмяРеквизитОбъекта, ПоставляемыеСведения, ЛокализуемыеРеквизитыОбъекта, ПараметрыРегистрации)
	
	Для каждого ИмяРеквизитаОбъекта Из ИмяРеквизитОбъекта Цикл
		
		Если СтрСравнить(ИмяРеквизитаОбъекта, "Предопределенный") = 0
			Или СтрСравнить(ИмяРеквизитаОбъекта, "Ссылка") = 0 Тогда
				Продолжить;
		КонецЕсли;
		
		ИмяРеквизитаБезСуффиксаЯзык= ИмяРеквизитаОбъекта;
		Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
			МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
			ИмяРеквизитаБезСуффиксаЯзык = МодульМультиязычностьСервер.ИмяРеквизитаБезСуффиксаЯзык(ИмяРеквизитаОбъекта);
		КонецЕсли;

		Если ЛокализуемыеРеквизитыОбъекта[ИмяРеквизитаБезСуффиксаЯзык] = Неопределено Тогда
		
			Если ТипЗнч(ПоставляемыеСведения[ИмяРеквизитаОбъекта]) = Тип("ТаблицаЗначений")
				И ТипЗнч(ДанныеЗапроса[ИмяРеквизитаОбъекта]) = Тип("РезультатЗапроса") Тогда
				
				Если ПараметрыРегистрации.СравниватьТабличныеЧасти Тогда
					ТаблицаДанных = ДанныеЗапроса[ИмяРеквизитаОбъекта].Выгрузить();
					Если Не ОбщегоНазначения.КоллекцииИдентичны(ПоставляемыеСведения[ИмяРеквизитаОбъекта], ТаблицаДанных) Тогда
						Возврат Истина;
					КонецЕсли;
				КонецЕсли;
				
			Иначе
				Если ПараметрыРегистрации.ПропускатьПустые И ПустаяСтрока(ПоставляемыеСведения[ИмяРеквизитаОбъекта]) Тогда
					Продолжить;
				КонецЕсли;
				Если ПоставляемыеСведения[ИмяРеквизитаОбъекта] <> ДанныеЗапроса[ИмяРеквизитаОбъекта] Тогда
					Возврат Истина;
				КонецЕсли;
			КонецЕсли;
		
		Иначе
			
			Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
				МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
				Если МодульМультиязычностьСервер.МультиязычныеСтрокиРеквизитаИзменены(ПоставляемыеСведения, ДанныеЗапроса,
						ИмяРеквизитаБезСуффиксаЯзык, ПараметрыРегистрации) Тогда
					Возврат Истина;
				КонецЕсли;
			КонецЕсли;
			
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Общие для начального заполнения


// Переопределяемые настройки заполнения элементов
// 
// Возвращаемое значение:
//  Структура:
//    * ПриНачальномЗаполненииЭлемента - Булево
//    * ИмяКлючевогоРеквизита - Строка
//    * ДополнительныеПараметры - Структура
//
Функция ПользовательскиеНастройкиЗаполненияЭлементов()
	
	НастройкиЗаполненияЭлементов = Новый Структура;
	НастройкиЗаполненияЭлементов.Вставить("ПриНачальномЗаполненииЭлемента", Ложь);
	НастройкиЗаполненияЭлементов.Вставить("ИмяКлючевогоРеквизита",          "ИмяПредопределенныхДанных");
	НастройкиЗаполненияЭлементов.Вставить("ДополнительныеПараметры", Новый Структура);

	Возврат НастройкиЗаполненияЭлементов;
	
КонецФункции

Процедура ЗаполнитьРеквизитыНачальнымиДанными(ЗаполняемыйЭлемент, НаборДанныхДляЗаполнения, НастройкиЗаполнения)
	
	ИмяКлючевогоРеквизита = ИмяКлючевогоРеквизита(НаборДанныхДляЗаполнения.НастройкиПредопределенныхЭлементов);
	
	ЗначениеКлючаОбъекта = ЗаполняемыйЭлемент[ИмяКлючевогоРеквизита];
	
	Если Не ЗначениеЗаполнено(ЗначениеКлючаОбъекта) Тогда
		
		Если Не НаборДанныхДляЗаполнения.НастройкиПредопределенныхЭлементов.ЕстьКолонкаИмяПредопределенныхДанных Тогда 
			Возврат;
		КонецЕсли;
		
		ИмяКлючевогоРеквизита = "ИмяПредопределенныхДанных";
		ЗначениеКлючаОбъекта = ЗаполняемыйЭлемент[ИмяКлючевогоРеквизита];
		Если Не ЗначениеЗаполнено(ЗначениеКлючаОбъекта) Тогда
			Возврат;
		КонецЕсли;
		
	КонецЕсли;
	
	МенеджерОбъекта = НаборДанныхДляЗаполнения.МенеджерОбъекта;
	
	СтрокаТаблицы = НаборДанныхДляЗаполнения.ПредопределенныеДанные.Найти(ЗначениеКлючаОбъекта, ИмяКлючевогоРеквизита);
	Если СтрокаТаблицы = Неопределено Тогда
		Возврат;
	КонецЕсли;
		
	КромеРеквизитов = НаборДанныхДляЗаполнения.РеквизитыИсключения;
	
	Если НаборДанныхДляЗаполнения.ПоддерживаетсяИерархия И ЗаполняемыйЭлемент.ЭтоГруппа Тогда
		
		КромеРеквизитов = НаборДанныхДляЗаполнения.РеквизитыИсключения
		+ ?(ПустаяСтрока(НаборДанныхДляЗаполнения.РеквизитыИсключения), "", ",") + СтрСоединить(НаборДанныхДляЗаполнения.РеквизитыСЭлементами, ",");
	КонецЕсли;
	
	Если Не НастройкиЗаполнения.ОбновитьТолькоМультиязычныеСтроки Тогда
		
		Если ЗначениеЗаполнено(НастройкиЗаполнения.Реквизиты) Тогда
			ЗаполнитьЗначенияСвойств(ЗаполняемыйЭлемент, СтрокаТаблицы, НастройкиЗаполнения.Реквизиты);
		Иначе
			ЗаполнитьЗначенияСвойств(ЗаполняемыйЭлемент, СтрокаТаблицы,, КромеРеквизитов);
		КонецЕсли;      
		
		Если НаборДанныхДляЗаполнения.ПоддерживаетсяИерархия И ЗначениеЗаполнено(СтрокаТаблицы.Родитель) Тогда
			
			Если ИмяКлючевогоРеквизита = "Ссылка" Тогда
				Если ТипЗнч(СтрокаТаблицы.Родитель) = Тип("Строка") Тогда
					ЗаполняемыйЭлемент.Родитель = МенеджерОбъекта.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаТаблицы.Родитель));
				ИначеЕсли ТипЗнч(СтрокаТаблицы.Родитель) = Тип("УникальныйИдентификатор") Тогда
					ЗаполняемыйЭлемент.Родитель = МенеджерОбъекта.ПолучитьСсылку(СтрокаТаблицы.Родитель);
				Иначе
					ЗаполняемыйЭлемент.Родитель = СтрокаТаблицы.Родитель;
				КонецЕсли;
			ИначеЕсли ТипЗнч(СтрокаТаблицы.Родитель) = Тип("Строка") Тогда 
				
				МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипЗнч(ЗаполняемыйЭлемент));
				СуществующиеЭлементы = СуществующиеПоставляемыеЭлементы(НаборДанныхДляЗаполнения.ПредопределенныеДанные,
					НаборДанныхДляЗаполнения.НастройкиПредопределенныхЭлементов, МенеджерОбъекта, МетаданныеОбъекта);
				ЗаполняемыйЭлемент.Родитель = СуществующиеЭлементы[СтрокаТаблицы.Родитель];
				
			Иначе
				ЗаполняемыйЭлемент.Родитель = СтрокаТаблицы.Родитель;
			КонецЕсли;
			
		КонецЕсли; 
		
		Если НЕ (НаборДанныхДляЗаполнения.ПоддерживаетсяИерархия И ЗаполняемыйЭлемент.ЭтоГруппа) Тогда
			Для Каждого ИмяТабличнойЧасти Из НаборДанныхДляЗаполнения.ТабличныеЧасти Цикл
				ЗаполняемыйЭлемент[ИмяТабличнойЧасти].Загрузить(СтрокаТаблицы[ИмяТабличнойЧасти]);
			КонецЦикла;
		КонецЕсли;
		
	КонецЕсли;
		
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
		МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
		МодульМультиязычностьСервер.ЗаполнитьЭлементыМультиязычнымиНачальнымиДанными(ЗаполняемыйЭлемент,
			НаборДанныхДляЗаполнения.ЛокализуемыеРеквизитыОбъекта, НаборДанныхДляЗаполнения.МультиязычныеСтрокиВРеквизитах,
			НаборДанныхДляЗаполнения.ПоддерживаетсяИерархия, СтрокаТаблицы);
	КонецЕсли;
	
	Если НаборДанныхДляЗаполнения.НастройкиПредопределенныхЭлементов.ПереопределяемыеНастройки.ПриНачальномЗаполненииЭлемента Тогда
		МенеджерОбъекта.ПриНачальномЗаполненииЭлемента(ЗаполняемыйЭлемент, СтрокаТаблицы, НаборДанныхДляЗаполнения.НастройкиПредопределенныхЭлементов.ПереопределяемыеНастройки.ДополнительныеПараметры);
	КонецЕсли;
	
КонецПроцедуры

// Обновить элементы предопределенных элементов в обработчиках обновления
// 
// Параметры:
//  СсылкиНаОбъекты - ВыборкаИзРезультатаЗапроса - ссылки на объекты:
//    * Ссылка - ЛюбаяСсылка
//  МетаданныеОбъекта - ОбъектМетаданных
//  НастройкиЗаполнения - см. ОбновлениеИнформационнойБазы.НастройкиЗаполнения
// 
// Возвращаемое значение:
//  Структура - обновить элементы предопределенных элементов:
//   * ПроблемныхОбъектов - Число
//   * ОбъектовОбработано - Число
//
Функция ОбновитьЭлементыПредопределенныхЭлементов(СсылкиНаОбъекты, МетаданныеОбъекта, НастройкиЗаполнения)
	
	Результат = Новый Структура();
	Результат.Вставить("ПроблемныхОбъектов", 0);
	Результат.Вставить("ОбъектовОбработано", 0);
	
	ПараметрыДляЗаполненияОбъекта = НаборПараметровДляЗаполненияОбъекта(МетаданныеОбъекта);
	
	ИмяКлючевогоРеквизита = ИмяКлючевогоРеквизита(ПараметрыДляЗаполненияОбъекта.НастройкиПредопределенныхЭлементов);
	
	ПоддерживаетсяИерархия = ПараметрыДляЗаполненияОбъекта.ПоддерживаетсяИерархия;
	РеквизитыИсключения = ПараметрыДляЗаполненияОбъекта.РеквизитыИсключения;
	МенеджерОбъекта = ПараметрыДляЗаполненияОбъекта.МенеджерОбъекта;
	
	Пока СсылкиНаОбъекты.Следующий() Цикл
		
		СсылкаНаОбъект = СсылкиНаОбъекты.Ссылка;
		
		ЗначениеКлючевогоРеквизита = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаОбъект, ИмяКлючевогоРеквизита);
		Если Не ЗначениеЗаполнено(ЗначениеКлючевогоРеквизита) Тогда
			Результат.ОбъектовОбработано = Результат.ОбъектовОбработано + 1;
			ОбновлениеИнформационнойБазы.ОтметитьВыполнениеОбработки(СсылкиНаОбъекты.Ссылка);
			Продолжить;
		КонецЕсли;
		
		СтрокаТаблицы = ПараметрыДляЗаполненияОбъекта.ПредопределенныеДанные.Найти(ЗначениеКлючевогоРеквизита, ИмяКлючевогоРеквизита);
		Если СтрокаТаблицы = Неопределено Тогда
			Результат.ОбъектовОбработано = Результат.ОбъектовОбработано + 1;
			ОбновлениеИнформационнойБазы.ОтметитьВыполнениеОбработки(СсылкиНаОбъекты.Ссылка);
			Продолжить;
		КонецЕсли;
		
		ПредставлениеСсылки = Строка(СсылкиНаОбъекты.Ссылка);
		
		НачатьТранзакцию();
		
		Попытка
			
			БлокировкаДанных = Новый БлокировкаДанных;
			ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(МетаданныеОбъекта.ПолноеИмя());
			ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", СсылкиНаОбъекты.Ссылка);
			БлокировкаДанных.Заблокировать();
			
			ЗаполняемыйЭлемент = СсылкиНаОбъекты.Ссылка.ПолучитьОбъект();
			
			КромеРеквизитов = РеквизитыИсключения;
			Если ПоддерживаетсяИерархия И ЗаполняемыйЭлемент.ЭтоГруппа Тогда
				КромеРеквизитов = РеквизитыИсключения
				+ ?(ПустаяСтрока(РеквизитыИсключения), "", ",") + СтрСоединить(ПараметрыДляЗаполненияОбъекта.РеквизитыСЭлементами, ",");
			КонецЕсли;
			
			Если ЗначениеЗаполнено(НастройкиЗаполнения.Реквизиты) Тогда
				ЗаполнитьЗначенияСвойств(ЗаполняемыйЭлемент, СтрокаТаблицы, НастройкиЗаполнения.Реквизиты);
			Иначе
				ЗаполнитьЗначенияСвойств(ЗаполняемыйЭлемент, СтрокаТаблицы,, КромеРеквизитов);
			КонецЕсли;
			
			Если НЕ (ПоддерживаетсяИерархия И ЗаполняемыйЭлемент.ЭтоГруппа) Тогда
				Для Каждого ИмяТабличнойЧасти Из ПараметрыДляЗаполненияОбъекта.ТабличныеЧасти Цикл
					ЗаполняемыйЭлемент[ИмяТабличнойЧасти].Загрузить(СтрокаТаблицы[ИмяТабличнойЧасти]);
				КонецЦикла;
			КонецЕсли;
			
			Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
				МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
				МодульМультиязычностьСервер.ЗаполнитьЭлементыМультиязычнымиНачальнымиДанными(ЗаполняемыйЭлемент,
					ПараметрыДляЗаполненияОбъекта.ЛокализуемыеРеквизитыОбъекта, ПараметрыДляЗаполненияОбъекта.МультиязычныеСтрокиВРеквизитах, 
					ПоддерживаетсяИерархия, СтрокаТаблицы);
			КонецЕсли;
			
			Если ПараметрыДляЗаполненияОбъекта.НастройкиПредопределенныхЭлементов.ПереопределяемыеНастройки.ПриНачальномЗаполненииЭлемента Тогда
				МенеджерОбъекта.ПриНачальномЗаполненииЭлемента(ЗаполняемыйЭлемент, СтрокаТаблицы, 
					ПараметрыДляЗаполненияОбъекта.НастройкиПредопределенныхЭлементов.ПереопределяемыеНастройки.ДополнительныеПараметры);
			КонецЕсли;
			
			ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ЗаполняемыйЭлемент);
			ЗафиксироватьТранзакцию();
			
		Исключение
			ОтменитьТранзакцию();
			// Если не удалось обработать элемент, повторяем попытку снова.
			Результат.ПроблемныхОбъектов = Результат.ПроблемныхОбъектов + 1;
			
			ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось заполнить элемент: %1 по причине: %2'"),
			ПредставлениеСсылки, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			
			ЗаписьЖурналаРегистрации(ОбновлениеИнформационнойБазы.СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Предупреждение,
			МетаданныеОбъекта, СсылкиНаОбъекты.Ссылка, ТекстСообщения);
		КонецПопытки;
		
		Результат.ОбъектовОбработано = Результат.ОбъектовОбработано + 1;
		
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция СуществующиеПоставляемыеЭлементы(ПредопределенныеДанные, НастройкиПредопределенныхЭлементов, Знач МенеджерОбъекта, МетаданныеОбъекта)
	
	Результат = Новый Соответствие();
	
	ИмяКлючевогоРеквизита = НастройкиПредопределенныхЭлементов.ПереопределяемыеНастройки.ИмяКлючевогоРеквизита;
	ЕстьКолонкаИмяПредопределенныхДанных = НастройкиПредопределенныхЭлементов.ЕстьКолонкаИмяПредопределенныхДанных;
	
	ИменаПредопределенных = МетаданныеОбъекта.ПолучитьИменаПредопределенных();
	
	НаборКлючей = Новый ТаблицаЗначений();
	Если ИмяКлючевогоРеквизита <> "Ссылка" Тогда
		НаборКлючей.Колонки.Добавить("Ключ", ОбщегоНазначения.ОписаниеТипаСтрока(150));
	Иначе
		
		Массив = Новый Массив;
		Массив.Добавить(ТипЗнч(МенеджерОбъекта.ПустаяСсылка()));
		НаборКлючей.Колонки.Добавить("Ключ",  Новый ОписаниеТипов(Массив));
	
	КонецЕсли;
	
	Результат = Новый Соответствие();
	
	Если СтрСравнить(ИмяКлючевогоРеквизита, "ИмяПредопределенныхДанных") <> 0 Тогда
		
		Для Каждого ПредопределенныйЭлемент Из ПредопределенныеДанные Цикл
			
			Если ЕстьКолонкаИмяПредопределенныхДанных 
				 И ЗначениеЗаполнено(ПредопределенныйЭлемент.ИмяПредопределенныхДанных) 
				 И ИменаПредопределенных.Найти(ПредопределенныйЭлемент.ИмяПредопределенныхДанных) <> Неопределено Тогда
					Результат.Вставить(ПредопределенныйЭлемент.ИмяПредопределенныхДанных, МенеджерОбъекта[ПредопределенныйЭлемент.ИмяПредопределенныхДанных]);
					Продолжить;
			КонецЕсли;
			
			Если ЗначениеЗаполнено(ПредопределенныйЭлемент[ИмяКлючевогоРеквизита]) Тогда
				
				НоваяСтрока = НаборКлючей.Добавить();
				НоваяСтрока.Ключ =  ПредопределенныйЭлемент[ИмяКлючевогоРеквизита];
			КонецЕсли;
			
		КонецЦикла;
		
		Если НаборКлючей.Количество() > 0 Тогда
			
			Запрос = Новый Запрос;
			ТекстЗапроса = "ВЫБРАТЬ
				|	НаборКлючей.Ключ КАК ИмяКлючевогоРеквизита
				|ПОМЕСТИТЬ НаборКлючей
				|ИЗ
				|	&НаборКлючей КАК НаборКлючей
				|;
				|
				|////////////////////////////////////////////////////////////////////////////////
				|ВЫБРАТЬ
				|	ЕСТЬNULL(ТаблицаДанных.Ссылка, НЕОПРЕДЕЛЕНО) КАК Ссылка,
				|	НаборКлючей.ИмяКлючевогоРеквизита КАК ИмяКлючевогоРеквизита
				|ИЗ
				|	НаборКлючей КАК НаборКлючей
				|		ЛЕВОЕ СОЕДИНЕНИЕ #Таблица КАК ТаблицаДанных
				|		ПО НаборКлючей.ИмяКлючевогоРеквизита = &ИмяПоля";
			
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИмяПоля", "ТаблицаДанных." + ИмяКлючевогоРеквизита);
			Запрос.Текст = СтрЗаменить(ТекстЗапроса, "#Таблица", МетаданныеОбъекта.ПолноеИмя());
			Запрос.УстановитьПараметр("НаборКлючей", НаборКлючей);
			
			РезультатЗапроса = Запрос.Выполнить();
			
			ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
			
			Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
				Результат.Вставить(ВыборкаДетальныеЗаписи.ИмяКлючевогоРеквизита, ВыборкаДетальныеЗаписи.Ссылка);
			КонецЦикла;
		
		КонецЕсли;
		
	ИначеЕсли ЕстьКолонкаИмяПредопределенныхДанных Тогда
		
		Для Каждого ПоставляемыйЭлемент Из ПредопределенныеДанные Цикл
			
			Если ЗначениеЗаполнено(ПоставляемыйЭлемент["ИмяПредопределенныхДанных"]) Тогда
					Результат.Вставить(ПоставляемыйЭлемент["ИмяПредопределенныхДанных"], МенеджерОбъекта[ПоставляемыйЭлемент[ИмяКлючевогоРеквизита]]);
					Продолжить;
			КонецЕсли;
		КонецЦикла;
		
	КонецЕсли;
	
	Возврат Результат;

КонецФункции

Процедура ДобавитьКолонкуТаблицыПредопределенныхДанных(ПредопределенныеДанные, Реквизит, ЛокализуемыеРеквизиты, Языки)
	
	Если СтрСравнить(Реквизит.Имя, "Родитель") = 0 Тогда
		МассивТипов = Новый Массив;
		Для Каждого ТипРеквизита Из Реквизит.Тип.Типы() Цикл
			МассивТипов.Добавить(ТипРеквизита);
		КонецЦикла;
		МассивТипов.Добавить(Тип("Строка"));

		ПараметрыСтроки = Новый КвалификаторыСтроки(150);
		ТипРеквизита = Новый ОписаниеТипов(МассивТипов,, ПараметрыСтроки);
	ИначеЕсли СтрСравнить(Реквизит.Имя, "Ссылка") = 0 Тогда
		МассивТипов = Новый Массив;
		Для Каждого ТипРеквизита Из Реквизит.Тип.Типы() Цикл
			МассивТипов.Добавить(ТипРеквизита);
		КонецЦикла;
		МассивТипов.Добавить(Тип("Строка"));
		МассивТипов.Добавить(Тип("УникальныйИдентификатор"));

		ПараметрыСтроки = Новый КвалификаторыСтроки(150);
		ТипРеквизита = Новый ОписаниеТипов(МассивТипов,, ПараметрыСтроки);
	Иначе
		ТипРеквизита = Реквизит.Тип;
	КонецЕсли;

	ПредопределенныеДанные.Колонки.Добавить(Реквизит.Имя, ТипРеквизита);
	Если ЛокализуемыеРеквизиты[Реквизит.Имя] = Неопределено Тогда
		Возврат;
	КонецЕсли;

	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
		МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
		ПризнакЛокализуемогоРеквизита = МодульМультиязычностьСервер.ПризнакЛокализуемогоРеквизита()
	Иначе
		ПризнакЛокализуемогоРеквизита = Неопределено;
	КонецЕсли;
	
	Для Каждого Язык Из Языки Цикл
		ПредопределенныеДанные.Колонки.Добавить(Реквизит.Имя + "_" + Язык, Реквизит.Тип, ПризнакЛокализуемогоРеквизита);
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// ОБРАБОТЧИКИ ОБНОВЛЕНИЯ ИНФОРМАЦИОННОЙ БАЗЫ

// Устанавливает ключ регламентного задания ОтложенноеОбновлениеИБ.
//
Процедура УстановитьКлючРегламентногоЗадания() Экспорт
	
	Отбор = Новый Структура;
	Отбор.Вставить("Метаданные", Метаданные.РегламентныеЗадания.ОтложенноеОбновлениеИБ);
	Отбор.Вставить("Предопределенное", Истина);
	Задания = РегламентныеЗаданияСервер.НайтиЗадания(Отбор);
	Для Каждого Задание Из Задания Цикл
		Если ЗначениеЗаполнено(Задание.Ключ) Тогда
			Продолжить;
		КонецЕсли;
		Задание.Ключ = Метаданные.РегламентныеЗадания.ОтложенноеОбновлениеИБ.Ключ;
		Задание.Записать(); // АПК:1363 регламентное задание не участвует в обменах.
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Очистка устаревших данных (предотвращение ошибки реструктуризации
// "Записи регистра стали неуникальными")

// Обработчик регламентного задания ОчисткаУстаревшихДанных (разделенного и неразделенного).
Процедура ОчисткаУстаревшихДанных() Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(
		Метаданные.РегламентныеЗадания.ОчисткаУстаревшихДанных);
	
	Если ТранзакцияАктивна() Тогда
		ТекстОшибки = НСтр("ru = 'Очистка устаревших данных не может выполняться во внешней транзакции.'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	МетаданныеЗадания = Метаданные.РегламентныеЗадания.ОчисткаУстаревшихДанных;
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Процедура ""%1"" может запускаться только в фоновом задании.'"),
		МетаданныеЗадания.ИмяМетода);
	
	ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
	Если ТекущийСеанс.ИмяПриложения <> "BackgroundJob" Тогда
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ТекущееФоновоеЗадание = ТекущийСеанс.ПолучитьФоновоеЗадание();
	Если ТекущееФоновоеЗадание = Неопределено Тогда
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		ПрогрессОбновления = Неопределено; // см. ОбновлениеИнформационнойБазы.ПрогрессОбновленияОбластейДанных
		Если Не ОтложенноеОбновлениеЗавершено(ПрогрессОбновления) Тогда
			Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Очистка устаревших неразделенных данных отложена, так как не во всех
				           |областях данных успешно завершилось отложенное обновление информационной базы.
				           |Областей с выполнением обновления: %1
				           |Областей с ожиданием обновления: %2
				           |Областей с проблемами обновления: %3'"),
				Формат(ПрогрессОбновления.Выполняется, "ЧН=0; ЧГ="),
				Формат(ПрогрессОбновления.Ожидают, "ЧН=0; ЧГ="),
				Формат(ПрогрессОбновления.Проблемы, "ЧН=0; ЧГ="));
			ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Очистка устаревших данных.Ожидание завершения обновления'",
					ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Информация,,, Комментарий);
			Возврат;
		КонецЕсли;
	ИначеЕсли Не ОтложенноеОбновлениеЗавершено() Тогда
		НастроитьЗаданиеОчисткаУстаревшихДанныхБезПопытки(Ложь);
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Очистка устаревших данных недопустима до успешного завершения
			           |отложенного обновления информационной базы.
			           |1. Результаты обновления тут: %1
			           |2. Вручную можно очистить тут: %2'"),
			"e1cib/app/Обработка.РезультатыОбновленияПрограммы",
			"e1cib/app/Обработка.РезультатыОбновленияПрограммы.Форма.ОчисткаУстаревшихДанных");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ИдентификаторЗадания = ТекущееФоновоеЗадание.УникальныйИдентификатор;
	НайденноеЗадание = Неопределено;
	Если ЗаданиеУжеВыполняется(МетаданныеЗадания.ИмяМетода, ИдентификаторЗадания,, НайденноеЗадание)
	 Или ЗаданиеУжеВыполняется("",, КлючЗаданияОчисткиУстаревшихДанных(), НайденноеЗадание) Тогда
		НастроитьЗаданиеОчисткаУстаревшихДанныхБезПопытки(Истина);
		Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Очистка устаревших данных уже выполняется
			           |в фоновом задании ""%1"" от %2'"),
			НайденноеЗадание.Наименование,
			Формат(НайденноеЗадание.Начало, "ДЛФ=DT"));
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Очистка устаревших данных.Отказ запуска'",
				ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Информация,,, Комментарий);
		Возврат;
	КонецЕсли;
	
	ГраницаВыполнения = ТекущаяДатаСеанса() + КоличествоМинутНепрерывнойОчисткиУстаревшихДанных() * 60;
	ВсеПорцииОбработаны = Истина;
	ЕстьУспехи = Ложь;
	Ошибки = Новый Массив;
	
	Контекст = Новый Структура("ЧиститьУдаляемые, ЭтоРегламентноеЗадание", Истина);
	Пока Истина Цикл
		НовыеПорции = Новый Соответствие;
		УстаревшиеДанныеПриЗапросеПорцийВФоне(НовыеПорции, Контекст);
		Если Не ЗначениеЗаполнено(НовыеПорции) Тогда
			Прервать;
		КонецЕсли;
		Для Каждого КлючИЗначение Из НовыеПорции Цикл
			Если ГраницаВыполнения < ТекущаяДатаСеанса() Тогда
				ВсеПорцииОбработаны = Ложь;
				Прервать;
			КонецЕсли;
			Порция = КлючИЗначение.Значение[0];
			Порция.Вставить("ГраницаВыполнения", ГраницаВыполнения);
			Порция.Вставить("ВсеПорцииОбработаны", Истина);
			Попытка
				УстаревшиеДанныеПриОчисткеПорцииВФоне(Порция);
				ЕстьУспехи = Истина;
			Исключение
				ИнформацияОбОшибке = ИнформацияОбОшибке();
				Ошибки.Добавить(ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
			КонецПопытки;
			Если Не Порция.ВсеПорцииОбработаны Тогда
				ВсеПорцииОбработаны = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Не ВсеПорцииОбработаны Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если Не ВсеПорцииОбработаны
	 Или ЗначениеЗаполнено(Ошибки) И ЕстьУспехи Тогда
		НастроитьЗаданиеОчисткаУстаревшихДанныхБезПопытки(Истина);
		Возврат;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Ошибки) Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не все данные удалось очистить, так как возникли ошибки:
			           |
			           |%1'"),
			СтрСоединить(Ошибки, "
			|--------------------------------------------------------------------------------
			|
			|"));
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Очистка устаревших данных.Ошибки удаления'",
				ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка,,, ТекстОшибки);
	КонецЕсли;
	
	НастроитьЗаданиеОчисткаУстаревшихДанныхБезПопытки(Ложь);
	
КонецПроцедуры

// Для процедуры ОчисткаУстаревшихДанных.
Функция КоличествоМинутНепрерывнойОчисткиУстаревшихДанных()
	Возврат 15;
КонецФункции

// Для процедур ОчисткаУстаревшихДанных и ВыполняетсяРегламентноеЗаданиеОчисткаУстаревшихДанных.
Функция ЗаданиеУжеВыполняется(ИмяМетода, ИдентификаторИсключаемогоЗадания = Неопределено,
			Ключ = Неопределено, НайденноеЗадание = Неопределено)
	
	Отбор = Новый Структура("Состояние", СостояниеФоновогоЗадания.Активно);
	Если ЗначениеЗаполнено(ИмяМетода) Тогда
		Отбор.Вставить("ИмяМетода", ИмяМетода);
	Иначе
		Отбор.Вставить("Ключ", Ключ);
	КонецЕсли;
	
	НайденныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);
	Для Каждого НайденноеЗадание Из НайденныеЗадания Цикл
		Если НайденноеЗадание.УникальныйИдентификатор <> ИдентификаторИсключаемогоЗадания Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для вызова из процедур обновления информационной базы.
Процедура НастроитьЗаданиеОчисткаУстаревшихДанных(Включить)
	
	Попытка
		НастроитьЗаданиеОчисткаУстаревшихДанныхБезПопытки(Включить);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось настроить регламентное задание
			           |""%1"" по причине:
			           |%2'"),
			Метаданные.РегламентныеЗадания.ОчисткаУстаревшихДанных.Имя,
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Очистка устаревших данных.Ошибка настройки регламентного задания'",
				ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка,,, ТекстОшибки);
	КонецПопытки;
	
КонецПроцедуры

// Для процедур НастроитьЗаданиеОчисткаУстаревшихДанных и ОчисткаУстаревшихДанных.
Процедура НастроитьЗаданиеОчисткаУстаревшихДанныхБезПопытки(Включить)
	
	МетаданныеЗадания = Метаданные.РегламентныеЗадания.ОчисткаУстаревшихДанных;
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Отбор = Новый Структура("ИмяМетода", МетаданныеЗадания.ИмяМетода);
	Иначе
		Отбор = Новый Структура("Метаданные", МетаданныеЗадания);
	КонецЕсли;
	НайденныеЗадания = РегламентныеЗаданияСервер.НайтиЗадания(Отбор);
	
	Если НайденныеЗадания.Количество() > 1 Тогда
		Для Каждого НайденноеЗадание Из НайденныеЗадания Цикл
			Если НайденноеЗадание = НайденныеЗадания[0] Тогда
				Продолжить;
			КонецЕсли;
			РегламентныеЗаданияСервер.УдалитьЗадание(НайденноеЗадание);
		КонецЦикла;
	КонецЕсли;
	
	Если НайденныеЗадания.Количество() = 0 Тогда
		НачатьТранзакцию();
		Попытка
			РегламентныеЗаданияСервер.ЗаблокироватьРегламентноеЗадание(МетаданныеЗадания);
			НайденныеЗадания = РегламентныеЗаданияСервер.НайтиЗадания(Отбор);
			Если НайденныеЗадания.Количество() = 0 Тогда
				// АПК:453-выкл - №760.4 Допустимо создавать задание без записи для получения свойств метаданных.
				НовоеЗадание = РегламентныеЗадания.СоздатьРегламентноеЗадание(МетаданныеЗадания);
				// АПК:453-вкл
				ПараметрыНовогоЗадания = Новый Структура("Ключ, ИнтервалПовтораПриАварийномЗавершении,
				|КоличествоПовторовПриАварийномЗавершении, Расписание, Метаданные");
				ЗаполнитьЗначенияСвойств(ПараметрыНовогоЗадания, НовоеЗадание);
				ПараметрыНовогоЗадания.Вставить("Использование", Включить);
				РегламентныеЗаданияСервер.ДобавитьЗадание(ПараметрыНовогоЗадания);
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки
	КонецЕсли;
	
	Если НайденныеЗадания.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Задание = НайденныеЗадания[0];
	Если Задание.Использование = Включить Тогда
		Возврат;
	КонецЕсли;
	
	ПараметрыЗадания = Новый Структура("Использование", Включить);
	
	НачатьТранзакцию();
	Попытка
		РегламентныеЗаданияСервер.ЗаблокироватьРегламентноеЗадание(Задание.УникальныйИдентификатор);
		НайденныеЗадания = РегламентныеЗаданияСервер.НайтиЗадания(Отбор);
		Если НайденныеЗадания.Количество() = 0
		 Или НайденныеЗадания[0].УникальныйИдентификатор <> Задание.УникальныйИдентификатор Тогда
			НастроитьЗаданиеОчисткаУстаревшихДанныхБезПопытки(Включить);
		ИначеЕсли НайденныеЗадания[0].Использование <> Включить Тогда
			РегламентныеЗаданияСервер.ИзменитьЗадание(НайденныеЗадания[0].УникальныйИдентификатор, ПараметрыЗадания);
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки
	
КонецПроцедуры

// Для процедуры ОчисткаУстаревшихДанных и вызова из формы.
Функция КлючЗаданияОчисткиУстаревшихДанных() Экспорт
	Возврат Новый УникальныйИдентификатор("f5104cf5-6251-438c-8557-e8bde0faec3e");
КонецФункции

// Для вызова из формы.
Процедура ОтменитьРегламентноеЗаданиеОчисткаУстаревшихДанных(ОтменитьУправляющееЗадание = Ложь) Экспорт
	
	Отбор = Новый Структура("Состояние, ИмяМетода", СостояниеФоновогоЗадания.Активно,
		Метаданные.РегламентныеЗадания.ОчисткаУстаревшихДанных.ИмяМетода);
	
	НайденныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);
	Для Каждого НайденноеЗадание Из НайденныеЗадания Цикл
		НайденноеЗадание.Отменить();
		НайденноеЗадание.ОжидатьЗавершенияВыполнения(7);
	КонецЦикла;
	
	Если Не ОтменитьУправляющееЗадание Тогда
		Возврат;
	КонецЕсли;
	
	Отбор = Новый Структура("Состояние, Ключ", СостояниеФоновогоЗадания.Активно,
		КлючЗаданияОчисткиУстаревшихДанных());
	
	НайденныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);
	Для Каждого НайденноеЗадание Из НайденныеЗадания Цикл
		НайденноеЗадание.Отменить();
		НайденноеЗадание.ОжидатьЗавершенияВыполнения(7);
	КонецЦикла;
	
КонецПроцедуры

// Для вызова после отложенного обновления.
Процедура ОчиститьПолностьюПослеУспешногоОтложенногоОбновления()
	
	ЗаголовокОшибки = НСтр("ru = 'Не удалось выполнить очистку устаревших данных.'")
		+ Символы.ПС + Символы.ПС;
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Процедура %1 не может вызываться в неразделенном режиме.'"),
			"ОчиститьПолностьюПослеУспешногоОтложенногоОбновления");
		ВызватьИсключение ЗаголовокОшибки + ТекстОшибки;
	КонецЕсли;
	
	ОтменитьРегламентноеЗаданиеОчисткаУстаревшихДанных(Истина);
	
	АдресРезультатаОчистки = ПоместитьВоВременноеХранилище(Неопределено, Новый УникальныйИдентификатор);
	
	ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне();
	ПараметрыВыполнения.ОжидатьЗавершение = Неопределено;
	ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Очистка устаревших данных (синхронно)'");
	ПараметрыВыполнения.АдресРезультата = АдресРезультатаОчистки;
	ПараметрыВыполнения.КлючФоновогоЗадания = КлючЗаданияОчисткиУстаревшихДанных();
	ПараметрыВыполнения.ЗапуститьНеВФоне = МонопольныйРежим();
	
	НаборПараметровПроцедуры = Новый Структура;
	НаборПараметровПроцедуры.Вставить("Контекст",
		Новый Структура("ЧиститьУдаляемые, СинхронноеОбновление", Ложь, Истина));
	НаборПараметровПроцедуры.Вставить("ИмяМетодаПолученияПорций",
		"ОбновлениеИнформационнойБазыСлужебный.УстаревшиеДанныеПриЗапросеПорцийВФоне");
	
	Попытка
		Результат = ДлительныеОперации.ВыполнитьПроцедуруВНесколькоПотоков(
			"ОбновлениеИнформационнойБазыСлужебный.УстаревшиеДанныеПриОчисткеПорцииВФоне",
			ПараметрыВыполнения, НаборПараметровПроцедуры);
		
		Если Результат.Статус = "Ошибка" Тогда
			ТекстОшибки = Результат.ПодробноеПредставлениеОшибки;
		ИначеЕсли Результат.Статус = "Отменено" Тогда
			ТекстОшибки = НСтр("ru = 'Задание было отменено'");
		ИначеЕсли Результат.Статус = "Выполняется" Тогда
			ТекстОшибки = НСтр("ru = 'Система не стала дожидаться завершения задания'");
		КонецЕсли;
		Если Результат.Статус <> "Выполнено" Тогда
			ВызватьИсключение ЗаголовокОшибки + ТекстОшибки;
		КонецЕсли;
		
		Результаты = ПолучитьИзВременногоХранилища(АдресРезультатаОчистки);
		ТекстОшибки = ТекстОшибкиОчисткиУстаревшихДанных(Результаты);
		Если ТекстОшибки <> Неопределено Тогда
			ВызватьИсключение ЗаголовокОшибки + ТекстОшибки;
		КонецЕсли;
		
	Исключение
		УдалитьИзВременногоХранилища(АдресРезультатаОчистки);
		ВызватьИсключение;
	КонецПопытки;
	
	УдалитьИзВременногоХранилища(АдресРезультатаОчистки);
	
КонецПроцедуры

// Для вызова из формы и процедуры ОчиститьПолностьюПослеУспешногоОтложенногоОбновления.
Функция ТекстОшибкиОчисткиУстаревшихДанных(Результаты) Экспорт
	
	Если ТипЗнч(Результаты) <> Тип("Соответствие") Тогда
		Если ОбщегоНазначения.РазделениеВключено() Тогда
			Возврат НСтр("ru = 'Фоновое задание не вернуло результат'");
		Иначе
			Возврат НСтр("ru = 'Управляющее фоновое задание не вернуло результат'");
		КонецЕсли;
	КонецЕсли;
	
	ЕстьОшибка = Ложь;
	ТекстыОшибок = Новый Массив;
	
	Для Каждого КлючИЗначение Из Результаты Цикл
		ТекущийРезультат = КлючИЗначение.Значение;
		Если ТекущийРезультат.Статус = "Ошибка" Тогда
			ТекстыОшибок.Добавить(ТекущийРезультат.КраткоеПредставлениеОшибки);
		ИначеЕсли ТекущийРезультат.Статус <> "Выполнено" Тогда
			ЕстьОшибка = Истина;
		КонецЕсли;
	КонецЦикла;
	
	ТекстОшибки = Неопределено;
	
	Если ЕстьОшибка И Не ЗначениеЗаполнено(ТекстыОшибок) Тогда
		ТекстОшибки = НСтр("ru = 'Некоторые задания не были выполнены, повторите операцию'");
	ИначеЕсли ЗначениеЗаполнено(ТекстыОшибок) Тогда
		ТекстОшибки = СтрСоединить(ТекстыОшибок, "
		|--------------------------------------------------------------------------------
		|
		|");
	КонецЕсли;
	
	Возврат ТекстОшибки;
	
КонецФункции

// Для вызова из управляющего потока многопоточной длительной операции,
// и для процедур ОчисткаУстаревшихДанных, ОчиститьУстаревшиеДанныеВФонеБезПопытки.
//
// Параметры:
//  НовыеПорции - Соответствие из КлючИЗначение:
//   * Ключ - УникальныйИдентификатор - новый уникальный ключ порции.
//   * Значение - Произвольный - параметр, передаваемый в процедуру обработки порции.
//
//  Контекст - Структура - при содержит свойства, которые были переданы при запуске,
//                длительной операции, можно вставлять новые, структура будет их сохранять,
//                кроме того, она будет передаваться в процедуру обработки порций.
//
Процедура УстаревшиеДанныеПриЗапросеПорцийВФоне(НовыеПорции, Контекст) Экспорт
	
	Если Не Контекст.Свойство("ОчищаемыеТаблицы") Тогда
		Если Не Контекст.Свойство("ЭтоРегламентноеЗадание") Тогда
			ВыполняетсяРегламентноеЗаданиеОчисткаУстаревшихДанных(Истина);
		КонецЕсли;
		Контекст.Вставить("ИндексТекущейТаблицы", -1);
		Контекст.Вставить("ОчищаемыеТаблицы", ОчищаемыеТаблицы(Не Контекст.ЧиститьУдаляемые));
		УстановитьПорядокОчисткиТаблиц(Контекст.ОчищаемыеТаблицы);
		ЗаписатьПланОчисткиВЖурналРегистрации(Контекст);
		Контекст.Вставить("РазмерПорции", 10000);
		Контекст.Вставить("МаксимальноеКоличествоПорций",
			?(Контекст.Свойство("Процент") И Не ОбщегоНазначения.ИнформационнаяБазаФайловая(), 5, 1));
		Свойства = Новый Массив;
		Для Каждого Колонка Из Контекст.ОчищаемыеТаблицы.Колонки Цикл
			Свойства.Добавить(Колонка.Имя);
		КонецЦикла;
		Контекст.Вставить("СвойстваОчищаемойТаблицы", СтрСоединить(Свойства, ","));
		Если Контекст.МаксимальноеКоличествоПорций = 1 Тогда
			Контекст.ОчищаемыеТаблицы.ЗаполнитьЗначения(Null, "ПоследняяСсылка");
		КонецЕсли;
	КонецЕсли;
	
	Пока Истина Цикл
		Контекст.ИндексТекущейТаблицы = Контекст.ИндексТекущейТаблицы + 1;
		Если Контекст.ИндексТекущейТаблицы >= Контекст.ОчищаемыеТаблицы.Количество() Тогда
			Прервать;
		КонецЕсли;
		ОчищаемаяТаблица = Контекст.ОчищаемыеТаблицы.Получить(Контекст.ИндексТекущейТаблицы);
		Если ТипЗнч(ОчищаемаяТаблица.Узлы) <> Тип("Массив") Тогда
			ОчищаемаяТаблица.Узлы = УзлыТаблицы(ОчищаемаяТаблица);
		КонецЕсли;
		Если ОчищаемаяТаблица.ЭтоРегистр
		   И (ОчищаемаяТаблица.ОчиститьВсе
		      Или ОчищаемаяТаблица.Независимый) Тогда
			Если Не ПродолжитьДобавлениеПорцийОчистки(Контекст, НовыеПорции, ОчищаемаяТаблица) Тогда
				Прервать;
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		ПрерватьВнешнийЦикл = Ложь;
		Пока Истина Цикл
			Запрос = ЗапросДанных(ОчищаемаяТаблица, Ложь, Ложь, Контекст.РазмерПорции);
			РезультатЗапроса = Запрос.Выполнить();
			Если РезультатЗапроса.Пустой() Тогда
				Прервать;
			КонецЕсли;
			ПорцияСсылок = РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка");
			Если Не ПродолжитьДобавлениеПорцийОчистки(Контекст, НовыеПорции, ОчищаемаяТаблица, ПорцияСсылок) Тогда
				ПрерватьВнешнийЦикл = Истина;
				Если ПорцияСсылок.Количество() = Контекст.РазмерПорции Тогда
					Контекст.ИндексТекущейТаблицы = Контекст.ИндексТекущейТаблицы - 1;
				КонецЕсли;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если ПрерватьВнешнийЦикл Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если Контекст.Свойство("Процент")
	   И Не Контекст.Свойство("СинхронноеОбновление") Тогда
		Контекст.Процент = ?(Контекст.ОчищаемыеТаблицы.Количество() = 0, 100,
			Окр((Контекст.ИндексТекущейТаблицы + 1) * 100 / Контекст.ОчищаемыеТаблицы.Количество()));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры УстаревшиеДанныеПриЗапросеПорцийВФоне.
Функция ВыполняетсяРегламентноеЗаданиеОчисткаУстаревшихДанных(ВызыватьИсключение = Ложь)
	
	МетаданныеЗадания = Метаданные.РегламентныеЗадания.ОчисткаУстаревшихДанных;
	Если Не ЗаданиеУжеВыполняется(МетаданныеЗадания.ИмяМетода) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Запуск невозможен, так как сейчас выполняется регламентное задание ""%1"".'"),
		МетаданныеЗадания.Представление());
	
	Если ВызыватьИсключение Тогда
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Для процедуры УстаревшиеДанныеПриЗапросеПорцийВФоне.
Процедура УстановитьПорядокОчисткиТаблиц(ОчищаемыеТаблицы)
	
	ОчищаемыеТаблицы.Сортировать("ЭтоРегистр Убыв, ОчиститьВсе Убыв, ЭтоПланОбмена Возр");
	
КонецПроцедуры

// Для процедуры УстаревшиеДанныеПриЗапросеПорцийВФоне.
Функция УзлыТаблицы(ОчищаемаяТаблица)
	
	Если Не ОчищаемаяТаблица.ВПланеОбмена Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ТекущаяТаблица.Узел КАК Узел
	|ИЗ
	|	&ТекущаяТаблица КАК ТекущаяТаблица";
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"&ТекущаяТаблица", ОчищаемаяТаблица.ПолноеИмя + ".Изменения");
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Узел");
	
КонецФункции

// Для процедуры УстаревшиеДанныеПриЗапросеПорцийВФоне.
Функция ПродолжитьДобавлениеПорцийОчистки(Контекст, НовыеПорции, ОчищаемаяТаблица, ПорцияСсылок = Неопределено)
	
	Если ОчищаемаяТаблица.ПоследняяСсылка <> Null
	   И ЗначениеЗаполнено(ПорцияСсылок) Тогда
		ОчищаемаяТаблица.ПоследняяСсылка = ПорцияСсылок[ПорцияСсылок.ВГраница()];
	КонецЕсли;
	
	Свойства = Новый Структура(Контекст.СвойстваОчищаемойТаблицы);
	ЗаполнитьЗначенияСвойств(Свойства, ОчищаемаяТаблица);
	
	НоваяПорция = Новый Структура;
	НоваяПорция.Вставить("ОчищаемаяТаблица", Свойства);
	НоваяПорция.Вставить("РазмерПорции", Контекст.РазмерПорции);
	НоваяПорция.Вставить("ПорцияСсылок", ПорцияСсылок);
	
	НовыеПорции.Вставить(Новый УникальныйИдентификатор,
		ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(НоваяПорция));
	
	Возврат НовыеПорции.Количество() < Контекст.МаксимальноеКоличествоПорций;
	
КонецФункции


// Для процедур ОчисткаУстаревшихДанных, ОчиститьУстаревшиеДанныеВФонеБезПопытки.
Процедура УстаревшиеДанныеПриОчисткеПорцииВФоне(Параметры) Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Истина, Ложь);
		Попытка
			УстаревшиеДанныеПриОчисткеПорции(Параметры);
		Исключение
			МодульУправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Ложь, Ложь);
			ВызватьИсключение;
		КонецПопытки;
		МодульУправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Ложь, Ложь);
	Иначе
		УстаревшиеДанныеПриОчисткеПорции(Параметры);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры УстаревшиеДанныеПриОчисткеПорцииВФоне.
Процедура УстаревшиеДанныеПриОчисткеПорции(Параметры)
	
	ОчищаемаяТаблица = Параметры.ОчищаемаяТаблица;
	ПорцияСсылок     = Параметры.ПорцияСсылок;
	РазмерПорции     = Параметры.РазмерПорции;
	
	Если ОчищаемаяТаблица.ЭтоРегистр Тогда
		МенеджерРегистра = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ОчищаемаяТаблица.ПолноеИмя);
		НаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
		ОбщегоНазначения.ОтключитьКонтрольЗаписи(НаборЗаписей);
	КонецЕсли;
	
	УзлыТаблицы = ОчищаемаяТаблица.Узлы;
	
	Если ОчищаемаяТаблица.ОчиститьВсе
	   И ОчищаемаяТаблица.ЭтоРегистр Тогда
		ЗаписатьСПопыткой(,, УзлыТаблицы,
			ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ОчищаемаяТаблица.ПолноеИмя));
		ЗаписатьСПопыткой(НаборЗаписей);
		Возврат;
	КонецЕсли;
	
	Если ОчищаемаяТаблица.Независимый Тогда
		Запрос = ЗапросДанных(ОчищаемаяТаблица, Ложь, Ложь, РазмерПорции);
		Для Каждого ОписаниеЗапроса Из Запрос Цикл
			ЗапросЗначенийИзмерения = ОписаниеЗапроса.Значение;
			ИмяИзмерения = ОписаниеЗапроса.Представление;
			Пока Истина Цикл
				РезультатЗапроса = ЗапросЗначенийИзмерения.Выполнить();
				Если РезультатЗапроса.Пустой() Тогда
					Прервать;
				КонецЕсли;
				Выборка = РезультатЗапроса.Выбрать();
				НаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
				ОбщегоНазначения.ОтключитьКонтрольЗаписи(НаборЗаписей);
				Пока Выборка.Следующий() Цикл
					ЭлементОтбора = НаборЗаписей.Отбор[ИмяИзмерения]; // ЭлементОтбора
					ЭлементОтбора.Установить(Выборка[ИмяИзмерения]);
					ЗаписатьСПопыткой(НаборЗаписей);
				КонецЦикла;
				Если Параметры.Свойство("ГраницаВыполнения")
				   И Параметры.ГраницаВыполнения < ТекущаяДатаСеанса() Тогда
					Параметры.ВсеПорцииОбработаны = Ложь;
					Возврат;
				КонецЕсли;
			КонецЦикла;
		КонецЦикла;
		
		Возврат;
	КонецЕсли;
	
	Если ОчищаемаяТаблица.ЭтоПланОбмена Тогда
		МетаданныеТаблицы = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ОчищаемаяТаблица.ПолноеИмя);
	КонецЕсли;
	
	Для Каждого Ссылка Из ПорцияСсылок Цикл
		Если ОчищаемаяТаблица.ЭтоРегистр Тогда
			ЭлементОтбора = НаборЗаписей.Отбор.Регистратор; // ЭлементОтбора
			ЭлементОтбора.Установить(Ссылка);
			ЗаписатьСПопыткой(,, УзлыТаблицы, НаборЗаписей);
			ЗаписатьСПопыткой(НаборЗаписей);
		Иначе
			Если Не ОчищаемаяТаблица.ЭтоПланОбмена Тогда
				ЗаписатьСПопыткой(,, УзлыТаблицы, Ссылка);
			ИначеЕсли Ссылка.ЭтотУзел <> Истина Тогда
				Для Каждого ЭлементСостава Из МетаданныеТаблицы.Состав Цикл
					ЗаписатьСПопыткой(,, Ссылка, ЭлементСостава.Метаданные);
				КонецЦикла;
			КонецЕсли;
			ЗаписатьСПопыткой(Ссылка, Истина,, ОчищаемаяТаблица.ЭтоПланОбмена);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры УстаревшиеДанныеПриОчисткеПорции.
Процедура ЗаписатьСПопыткой(Данные, Удалить = Ложь, УзлыТаблицы = "", ДанныеУзлов = Неопределено)
	
	НомерПопытки = 1;
	Пока Истина Цикл
		Попытка
			Если УзлыТаблицы <> "" Тогда
				УзлыТаблицы = УзлыТаблицы; // Массив
				ПланыОбмена.УдалитьРегистрациюИзменений(УзлыТаблицы, ДанныеУзлов);
			ИначеЕсли Удалить Тогда
				Данные = Данные; // СправочникСсылка
				ТекущийОбъект = Данные.ПолучитьОбъект();
				Если ТекущийОбъект <> Неопределено Тогда
					ОбщегоНазначения.ОтключитьКонтрольЗаписи(ТекущийОбъект, ДанныеУзлов);
					// АПК:1327-выкл - Допустимо так как в попытке с повтором, требуется для снижения взаимоблокировок СУБД
					ТекущийОбъект.Удалить();
					// АПК:1327-вкл
				КонецЕсли;
			Иначе
				Данные = Данные; // СправочникОбъект
				Данные.Записать();
			КонецЕсли;
		Исключение
			НомерПопытки = НомерПопытки + 1;
			ФоновоеЗадание = ПолучитьТекущийСеансИнформационнойБазы().ПолучитьФоновоеЗадание();
			Если ФоновоеЗадание = Неопределено
			 Или НомерПопытки > 10 Тогда
				ВызватьИсключение;
			КонецЕсли;
			ФоновоеЗадание.ОжидатьЗавершенияВыполнения(НомерПопытки * 3);
			Продолжить;
		КонецПопытки;
		Прервать;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры УстаревшиеДанныеПриЗапросеПорцийВФоне.
Процедура ЗаписатьПланОчисткиВЖурналРегистрации(Контекст)
	
	ПланОчистки = ПланОчисткиУстаревшихДанных(Контекст.ЧиститьУдаляемые, Контекст.ОчищаемыеТаблицы);
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Очистка устаревших данных.План очистки'",
			ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Информация,,, ПланОчистки);
	
КонецПроцедуры

// Для процедуры ЗаписатьПланОчисткиВЖурналРегистрации и вызова из формы.
Функция ПланОчисткиУстаревшихДанных(ЧиститьУдаляемые, ОчищаемыеТаблицы = Неопределено, УчестьРазделениеДанных = Истина) Экспорт
	
	Строки = Новый Массив;
	ДоступноИспользованиеРазделенныхДанных = ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	
	Если УчестьРазделениеДанных Или ДоступноИспользованиеРазделенныхДанных Тогда
		Если ОчищаемыеТаблицы = Неопределено Тогда
			ОчищаемыеТаблицы = ОчищаемыеТаблицы(Не ЧиститьУдаляемые);
			УстановитьПорядокОчисткиТаблиц(ОчищаемыеТаблицы);
		КонецЕсли;
		Если Не ОбщегоНазначения.РазделениеВключено() Тогда
			Строки.Добавить(НСтр("ru = 'Будут обработаны следующие таблицы'"));
		ИначеЕсли Не ДоступноИспользованиеРазделенныхДанных Тогда
			Строки.Добавить(НСтр("ru = 'Будут обработаны следующие неразделенные таблицы (общие данные)'"));
		Иначе
			Строки.Добавить(НСтр("ru = 'Будут обработаны следующие разделенные таблицы'"));
		КонецЕсли;
		ДобавитьОписаниеТаблицВПланОчистки(Строки, ОчищаемыеТаблицы);
	Иначе
		ВсеТаблицы = ОчищаемыеТаблицы(Не ЧиститьУдаляемые, Ложь);
		
		Отбор = Новый Структура("Неразделенный", Истина);
		НеразделенныеТаблицы = ВсеТаблицы.Скопировать(ВсеТаблицы.НайтиСтроки(Отбор));
		УстановитьПорядокОчисткиТаблиц(НеразделенныеТаблицы);
		Строки.Добавить(НСтр("ru = '1. Будут обработаны следующие неразделенные таблицы (общие данные).'"));
		ДобавитьОписаниеТаблицВПланОчистки(Строки, НеразделенныеТаблицы);
		
		Строки.Добавить("");
		
		Отбор = Новый Структура("Неразделенный", Истина);
		РазделенныеТаблицы = ВсеТаблицы.Скопировать(ВсеТаблицы.НайтиСтроки(Отбор));
		УстановитьПорядокОчисткиТаблиц(РазделенныеТаблицы);
		Строки.Добавить(НСтр("ru = '2. Будут обработаны следующие разделенные таблицы в областях данных
		                           |   (полный план очистки доступен только при входе в область данных).'"));
		ДобавитьОписаниеТаблицВПланОчистки(Строки, РазделенныеТаблицы);
	КонецЕсли;
	
	Возврат СтрСоединить(Строки, Символы.ПС);
	
КонецФункции

// Для функции ПланОчисткиУстаревшихДанных.
Процедура ДобавитьОписаниеТаблицВПланОчистки(Строки, ОчищаемыеТаблицы)
	
	Для Каждого ОчищаемаяТаблица Из ОчищаемыеТаблицы Цикл
		Строки.Добавить("");
		Строки.Добавить(ОчищаемаяТаблица.Представление + " (" + ОчищаемаяТаблица.ПолноеИмя + ")");
		Если ОчищаемаяТаблица.ОчиститьВсе Тогда
			Строки.Добавить("	" + НСтр("ru = 'Полная очистка'"));
			Продолжить;
		ИначеЕсли ОчищаемаяТаблица.Независимый Тогда
			Строки.Добавить("	" + НСтр("ru = 'Удаление записей по значениям в измерениях:'"));
			ДобавитьОписаниеПолейВПланОчистки(Строки, ОчищаемаяТаблица.ПоляРегистра);
			Продолжить;
		КонецЕсли;
		Если ЗначениеЗаполнено(ОчищаемаяТаблица.ПоляРегистра) Тогда
			Строки.Добавить("	" + НСтр("ru = 'Удаление записей по регистраторам при наличии значений в измерениях основной таблицы:'"));
			ДобавитьОписаниеПолейВПланОчистки(Строки, ОчищаемаяТаблица.ПоляРегистра);
		КонецЕсли;
		Если ЗначениеЗаполнено(ОчищаемаяТаблица.ПоляСубконто) Тогда
			Строки.Добавить("	" + НСтр("ru = 'Удаление записей по регистраторам при наличии значений в измерениях таблицы субконто:'"));
			ДобавитьОписаниеПолейВПланОчистки(Строки, ОчищаемаяТаблица.ПоляСубконто);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьОписаниеТаблицВПланОчистки.
Процедура ДобавитьОписаниеПолейВПланОчистки(Строки, ОписаниеПолей)
	
	Для Каждого ОписаниеПоля Из ОписаниеПолей Цикл
		Строки.Добавить("		" + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Измерение ""%1"" содержит значения следующих типов:'"), ОписаниеПоля.Ключ));
		Для Каждого ОписаниеТипа Из ОписаниеПоля.Значение Цикл
			Если ТипЗнч(ОписаниеТипа.Значение) = Тип("Массив") Тогда
				Строки.Добавить("			" + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Для типа ""%1"" проверяются только следующие значения:'"), Строка(ОписаниеТипа.Ключ)) + "
					|				" + СтрСоединить(ОписаниеТипа.Значение, "
					|				"));
			Иначе
				Строки.Добавить("			""" + Строка(ОписаниеТипа.Ключ) + """ (" + ОписаниеТипа.Значение + ")");
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры


// Обработчик фонового задания для модели сервиса.
Процедура ОчиститьУстаревшиеДанныеВФоне(Параметры, АдресРезультата) Экспорт
	
	ПоследняяОтправка = ТекущаяДатаСеанса();
	ПоследнийПроцент = 0;
	
	МодульРаботаВМоделиСервиса = Неопределено;
	Параметры.ОбрабатыватьОбластиДанных = Параметры.ОбрабатыватьОбластиДанных
		И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	
	Попытка
		ОчиститьУстаревшиеДанныеВФонеБезПопытки(Параметры);
		Если Параметры.ОбрабатыватьОбластиДанных Тогда
			МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
			ОбластиДанных = МодульРаботаВМоделиСервиса.ИспользуемыеОбластиДанных().Выгрузить().ВыгрузитьКолонку(
				"ОбластьДанных");
			Для Каждого ОбластьДанных Из ОбластиДанных Цикл
				МодульРаботаВМоделиСервиса.ВойтиВОбластьДанных(ОбластьДанных);
				ОчиститьУстаревшиеДанныеВФонеБезПопытки(Параметры); // @skip-check query-in-loop - Порционная обработка данных
				МодульРаботаВМоделиСервиса.ВыйтиИзОбластиДанных();
				Если ПоследняяОтправка + 5 < ТекущаяДатаСеанса() Тогда
					НовыйПроцент = Цел(ОбластиДанных.Найти(ОбластьДанных) / ОбластиДанных.Количество() * 100);
					Если НовыйПроцент > ПоследнийПроцент Тогда
						ДлительныеОперации.СообщитьПрогресс(НовыйПроцент);
						ПоследнийПроцент = НовыйПроцент;
					КонецЕсли;
					ПоследняяОтправка = ТекущаяДатаСеанса();
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Результат = Новый Соответствие;
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Результат = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
		Если Параметры.ОбрабатыватьОбластиДанных
		   И МодульРаботаВМоделиСервиса <> Неопределено Тогда
			МодульРаботаВМоделиСервиса.ВыйтиИзОбластиДанных();
		КонецЕсли;
	КонецПопытки;
	
	ПоместитьВоВременноеХранилище(Результат, АдресРезультата);
	
КонецПроцедуры

// Для процедуры ОчиститьУстаревшиеДанныеВФоне.
Процедура ОчиститьУстаревшиеДанныеВФонеБезПопытки(Параметры)

	ОбрабатыватьОбластиДанных = Параметры.ОбрабатыватьОбластиДанных;
	
	ПоследняяОтправка = ТекущаяДатаСеанса();
	ПоследнийПроцент = 0;
	
	Контекст = Новый Структура("ЧиститьУдаляемые", Параметры.ЧиститьУдаляемые);
	Пока Истина Цикл
		НовыеПорции = Новый Соответствие;
		УстаревшиеДанныеПриЗапросеПорцийВФоне(НовыеПорции, Контекст);
		Если Не ЗначениеЗаполнено(НовыеПорции) Тогда
			Прервать;
		КонецЕсли;
		Для Каждого КлючИЗначение Из НовыеПорции Цикл
			УстаревшиеДанныеПриОчисткеПорцииВФоне(КлючИЗначение.Значение[0]);
		КонецЦикла;
		Если Не ОбрабатыватьОбластиДанных
		   И ПоследняяОтправка + 5 < ТекущаяДатаСеанса() Тогда
			НовыйПроцент = Цел(Контекст.ИндексТекущейТаблицы / Контекст.ОчищаемыеТаблицы.Количество() * 100);
			Если НовыйПроцент > ПоследнийПроцент Тогда
				ДлительныеОперации.СообщитьПрогресс(НовыйПроцент);
				ПоследнийПроцент = НовыйПроцент;
			КонецЕсли;
			ПоследняяОтправка = ТекущаяДатаСеанса();
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры


// Обработчик фонового задания для вызова из формы.
Процедура СформироватьСписокУстаревшихДанныхВФоне(Параметры, АдресРезультата) Экспорт
	
	УстаревшиеДанные = Новый ТаблицаЗначений;
	УстаревшиеДанные.Колонки.Добавить("ПолноеИмяТаблицы",     Новый ОписаниеТипов("Строка"));
	УстаревшиеДанные.Колонки.Добавить("ПредставлениеТаблицы", Новый ОписаниеТипов("Строка"));
	УстаревшиеДанные.Колонки.Добавить("Количество",           Новый ОписаниеТипов("Число"));
	УстаревшиеДанные.Колонки.Добавить("ОбластьДанных",        Новый ОписаниеТипов("Число"));
	
	МодульРаботаВМоделиСервиса = Неопределено;
	Параметры.ОбрабатыватьОбластиДанных = Параметры.ОбрабатыватьОбластиДанных
		И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	
	ПоследняяОтправка = ТекущаяДатаСеанса();
	ПоследнийПроцент = 0;
	
	Попытка
		СформироватьСписокУстаревшихДанныхВФонеБезПопытки(УстаревшиеДанные, Параметры);
		Если Параметры.ОбрабатыватьОбластиДанных Тогда
			УстаревшиеДанные.ЗаполнитьЗначения(-1, "ОбластьДанных");
			МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
			ОбластиДанных = МодульРаботаВМоделиСервиса.ИспользуемыеОбластиДанных().Выгрузить().ВыгрузитьКолонку(
				"ОбластьДанных");
			Для Каждого ОбластьДанных Из ОбластиДанных Цикл
				УстаревшиеДанныеОбласти = УстаревшиеДанные.Скопировать(Новый Массив);
				МодульРаботаВМоделиСервиса.ВойтиВОбластьДанных(ОбластьДанных);
				СформироватьСписокУстаревшихДанныхВФонеБезПопытки(УстаревшиеДанныеОбласти, Параметры); // @skip-check query-in-loop - Порционная обработка данных
				МодульРаботаВМоделиСервиса.ВыйтиИзОбластиДанных();
				Если ЗначениеЗаполнено(УстаревшиеДанныеОбласти) Тогда
					НоваяСтрока = УстаревшиеДанные.Добавить();
					ЗаполнитьЗначенияСвойств(НоваяСтрока, УстаревшиеДанныеОбласти[0]);
					НоваяСтрока.ОбластьДанных = ОбластьДанных;
				КонецЕсли;
				Если ПоследняяОтправка + 5 < ТекущаяДатаСеанса() Тогда
					НовыйПроцент = Цел(ОбластиДанных.Найти(ОбластьДанных) / ОбластиДанных.Количество() * 100);
					Если НовыйПроцент > ПоследнийПроцент Тогда
						ДлительныеОперации.СообщитьПрогресс(НовыйПроцент);
						ПоследнийПроцент = НовыйПроцент;
					КонецЕсли;
					ПоследняяОтправка = ТекущаяДатаСеанса();
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Результат = УстаревшиеДанные;
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Результат = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
		Если Параметры.ОбрабатыватьОбластиДанных
		   И МодульРаботаВМоделиСервиса <> Неопределено Тогда
			МодульРаботаВМоделиСервиса.ВыйтиИзОбластиДанных();
		КонецЕсли;
	КонецПопытки;
	
	ПоместитьВоВременноеХранилище(Результат, АдресРезультата);
	
КонецПроцедуры

// Для процедуры СформироватьСписокУстаревшихДанныхВФоне.
Процедура СформироватьСписокУстаревшихДанныхВФонеБезПопытки(УстаревшиеДанные, Параметры)
	
	ОчищаемыеТаблицы = ОчищаемыеТаблицы(Не Параметры.ЧиститьУдаляемые);
	ОбщееКоличество = 0;
	ВывестиКоличество = Параметры.ВывестиКоличество;
	ОбрабатыватьОбластиДанных = Параметры.ОбрабатыватьОбластиДанных;
	
	ПоследняяОтправка = ТекущаяДатаСеанса();
	ПоследнийПроцент = 0;
	
	Для Каждого ОчищаемаяТаблица Из ОчищаемыеТаблицы Цикл
		Запрос = ЗапросДанных(ОчищаемаяТаблица, Истина, ВывестиКоличество);
		РезультатЗапроса = Запрос.Выполнить(); // @skip-check query-in-loop - Порционная обработка данных
		Количество = 0;
		Если ВывестиКоличество Тогда
			Выборка = РезультатЗапроса.Выбрать();
			Если Выборка.Следующий()
			   И ТипЗнч(Выборка.Количество) = Тип("Число") Тогда
				Количество = Выборка.Количество;
			КонецЕсли;
		ИначеЕсли Не РезультатЗапроса.Пустой() Тогда
			Количество = 1;
		КонецЕсли;
		Если Не ОбрабатыватьОбластиДанных
		   И ПоследняяОтправка + 5 < ТекущаяДатаСеанса() Тогда
			НовыйПроцент = Цел(ОчищаемыеТаблицы.Индекс(ОчищаемаяТаблица) / ОчищаемыеТаблицы.Количество() * 100);
			Если НовыйПроцент > ПоследнийПроцент Тогда
				ДлительныеОперации.СообщитьПрогресс(НовыйПроцент);
				ПоследнийПроцент = НовыйПроцент;
			КонецЕсли;
			ПоследняяОтправка = ТекущаяДатаСеанса();
		КонецЕсли;
		Если Не ЗначениеЗаполнено(Количество) Тогда
			Продолжить;
		КонецЕсли;
		ОбщееКоличество = ОбщееКоличество + Количество;
		Если Не ОбрабатыватьОбластиДанных Тогда
			НоваяСтрока = УстаревшиеДанные.Добавить();
			НоваяСтрока.ПолноеИмяТаблицы     = ОчищаемаяТаблица.ПолноеИмя;
			НоваяСтрока.ПредставлениеТаблицы = ОчищаемаяТаблица.Представление;
			НоваяСтрока.Количество = Количество;
		ИначеЕсли Не ВывестиКоличество Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если ОбрабатыватьОбластиДанных
	   И ОбщееКоличество > 0
	 Или ВывестиКоличество
	   И УстаревшиеДанные.Количество() > 1 Тогда
		
		Если Не ОбрабатыватьОбластиДанных Тогда
			Представление = НСтр("ru = 'Суммарное количество для всех таблиц'");
		ИначеЕсли ВывестиКоличество Тогда
			Представление = НСтр("ru = 'Суммарное количество для всех таблиц области'");
		Иначе
			Представление = НСтр("ru = 'Есть таблицы для очистки'");
		КонецЕсли;
		
		НоваяСтрока = УстаревшиеДанные.Вставить(0);
		НоваяСтрока.ПредставлениеТаблицы = "<" + Представление + ">";
		НоваяСтрока.Количество = ОбщееКоличество;
	КонецЕсли;
	
КонецПроцедуры


// Для процедур УстаревшиеДанныеПриЗапросеПорцийВФоне, УстаревшиеДанныеПриОчисткеПорции,
// СформироватьСписокУстаревшихДанныхВФонеБезПопытки.
//
// Параметры:
//  ОчищаемаяТаблица - СтрокаТаблицыЗначений из см. ОчищаемыеТаблицы
//
// Возвращаемое значение:
//  Запрос - запрос значений поля из основной таблицы
//  СписокЗначений:
//    Значение      - Запрос - запрос значений поля из основной таблицы регистра
//    Представление - Строка - имя поля независимого регистра сведений в режиме ТолькоНаличие = Ложь
//
Функция ЗапросДанных(ОчищаемаяТаблица, ТолькоНаличие = Истина, Количество = Ложь, РазмерПорции = 10000)
	
	ТекстЗапроса =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 10000
	|	&Поле
	|ИЗ
	|	&ТекущаяТаблица КАК ТекущаяТаблица
	|ГДЕ
	|	&Отбор";
	
	Запрос = Новый Запрос;
	ПрефиксИмениПараметра = СтрЗаменить(ОчищаемаяТаблица.ПолноеИмя, ".", "_") + "_";
	Поле = "";
	ЕстьРегистратор = ОчищаемаяТаблица.ЭтоРегистр И Не ОчищаемаяТаблица.Независимый;
	Если ТолькоНаличие И Количество Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПЕРВЫЕ 10000", "");
		Если ЕстьРегистратор Тогда
			Поле = "КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ТекущаяТаблица.Регистратор) КАК Количество";
		Иначе
			Поле = "КОЛИЧЕСТВО(*) КАК Количество";
		КонецЕсли;
	ИначеЕсли ТолькоНаличие Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "10000", "1");
		Поле = "ИСТИНА";
	ИначеЕсли ОчищаемаяТаблица.Независимый Тогда
		Поле = "";
	Иначе
		ИмяПоля = ?(ОчищаемаяТаблица.ЭтоРегистр, "Регистратор", "Ссылка");
		Поле = СтрЗаменить("ТекущаяТаблица.ИмяПоля КАК Ссылка", "ИмяПоля", ИмяПоля);
		Если ОчищаемаяТаблица.ПоследняяСсылка <> Null Тогда
			ТекстЗапроса = ТекстЗапроса + СтрЗаменить("
			|
			|УПОРЯДОЧИТЬ ПО
			|	ТекущаяТаблица.ИмяПоля", "ИмяПоля", ИмяПоля);
			Если ЗначениеЗаполнено(ОчищаемаяТаблица.ПоследняяСсылка) Тогда
				ИмяПараметра = ПрефиксИмениПараметра + "ПоследняяСсылка";
				ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Отбор", СтрЗаменить(
				"ТекущаяТаблица.ИмяПоля > &" + ИмяПараметра + "
				|	И (&Отбор)", "ИмяПоля", ИмяПоля));
				Запрос.УстановитьПараметр(ИмяПараметра, ОчищаемаяТаблица.ПоследняяСсылка);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "10000", Формат(РазмерПорции, "ЧГ="));
	
	Если Не ТолькоНаличие
	   И ОчищаемаяТаблица.Независимый
	   И ОчищаемаяТаблица.ПоляРегистра.Количество() > 0 Тогда
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
			"&ТекущаяТаблица", ОчищаемаяТаблица.ПолноеИмя);
		
		Запросы = Новый СписокЗначений;
		Для Каждого ОписаниеПоля Из ОчищаемаяТаблица.ПоляРегистра Цикл
			ПоляРегистра = Новый Соответствие;
			ПоляРегистра.Вставить(ОписаниеПоля.Ключ, ОписаниеПоля.Значение);
			Запрос = Новый Запрос;
			Запрос.Текст = СтрЗаменить(ТекстЗапроса, "&Поле",
				"ТекущаяТаблица." + ОписаниеПоля.Ключ + " КАК " + ОписаниеПоля.Ключ);
			УстановитьОтборВТекстЗапроса(ПоляРегистра, Запрос.Текст, Запрос, ПрефиксИмениПараметра);
			Запросы.Добавить(Запрос, ОписаниеПоля.Ключ);
		КонецЦикла;
		
		Возврат Запросы;
	КонецЕсли;
	
	Если ОчищаемаяТаблица.ЭтоРегистр
	   И ЗначениеЗаполнено(ОчищаемаяТаблица.ПоляРегистра)
	   И ЗначениеЗаполнено(ОчищаемаяТаблица.ПоляСубконто) Тогда
	
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Поле",
			"ТекущаяТаблица.Регистратор КАК Регистратор");
		Если ТолькоНаличие И Не Количество Тогда
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "РАЗЛИЧНЫЕ", "");
		КонецЕсли;
		ТекстЗапроса1 = СтрЗаменить(ТекстЗапроса,
			"&ТекущаяТаблица", ОчищаемаяТаблица.ПолноеИмя);
		ТекстЗапроса2 = СтрЗаменить(ТекстЗапроса,
			"&ТекущаяТаблица", ОчищаемаяТаблица.ПолноеИмя + ".Субконто");
		УстановитьОтборВТекстЗапроса(ОчищаемаяТаблица.ПоляРегистра,
			ТекстЗапроса1, Запрос, ПрефиксИмениПараметра);
		УстановитьОтборВТекстЗапроса(ОчищаемаяТаблица.ПоляСубконто,
			ТекстЗапроса2, Запрос, ПрефиксИмениПараметра + "2_");
		ТекстЗапроса =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 10000
		|	&Поле
		|ИЗ
		|	(ВЫБРАТЬ &ТекущаяТаблица1
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ &ТекущаяТаблица2) КАК ТекущаяТаблица";
		Если ТолькоНаличие И Количество Тогда
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "РАЗЛИЧНЫЕ ПЕРВЫЕ 10000", "");
		ИначеЕсли ТолькоНаличие Тогда
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "РАЗЛИЧНЫЕ ПЕРВЫЕ 10000", "ПЕРВЫЕ 1");
		КонецЕсли;
		Если ТолькоНаличие Или ОчищаемаяТаблица.ПоследняяСсылка = Null Тогда
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Поле", Поле);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ВЫБРАТЬ &ТекущаяТаблица1",
				СтрЗаменить(ТекстЗапроса1, Символы.ПС, Символы.ПС + Символы.Таб));
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ВЫБРАТЬ &ТекущаяТаблица2",
				СтрЗаменить(ТекстЗапроса2, Символы.ПС, Символы.ПС + Символы.Таб));
		Иначе
			ТекстЗапроса =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 10000
			|	ТекущаяТаблица.Регистратор КАК Ссылка
			|ИЗ
			|	(ВЫБРАТЬ
			|		ТекущаяТаблица1.Регистратор КАК Регистратор
			|	ИЗ
			|		ТекущаяТаблица1 КАК ТекущаяТаблица1
			|	
			|	ОБЪЕДИНИТЬ ВСЕ
			|	
			|	ВЫБРАТЬ
			|		ТекущаяТаблица2.Регистратор
			|	ИЗ
			|		ТекущаяТаблица2 КАК ТекущаяТаблица2) КАК ТекущаяТаблица
			|
			|УПОРЯДОЧИТЬ ПО
			|	ТекущаяТаблица.Регистратор";
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса1,
				"ТекущаяТаблица.Регистратор КАК Регистратор",
				"ТекущаяТаблица.Регистратор КАК Регистратор
				|ПОМЕСТИТЬ ТекущаяТаблица1")
				+ ОбщегоНазначения.РазделительПакетаЗапросов()
				+ СтрЗаменить(ТекстЗапроса2,
				"ТекущаяТаблица.Регистратор КАК Регистратор",
				"ТекущаяТаблица.Регистратор КАК Регистратор
				|ПОМЕСТИТЬ ТекущаяТаблица2")
				+ ОбщегоНазначения.РазделительПакетаЗапросов()
				+ ТекстЗапроса;
		КонецЕсли;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "10000", Формат(РазмерПорции, "ЧГ="));
	Иначе
		Если Не ЕстьРегистратор Или ТолькоНаличие Тогда
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "РАЗЛИЧНЫЕ", "");
		КонецЕсли;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
			"&ТекущаяТаблица", ОчищаемаяТаблица.ПолноеИмя
			+ ?(ЗначениеЗаполнено(ОчищаемаяТаблица.ПоляСубконто), ".Субконто", ""));
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Поле", Поле);
		Если ОчищаемаяТаблица.ЭтоРегистр Тогда
			УстановитьОтборВТекстЗапроса(?(ЗначениеЗаполнено(ОчищаемаяТаблица.ПоляСубконто),
				ОчищаемаяТаблица.ПоляСубконто, ОчищаемаяТаблица.ПоляРегистра),
				ТекстЗапроса, Запрос, ПрефиксИмениПараметра);
		Иначе
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Отбор", "ИСТИНА");
		КонецЕсли;
	КонецЕсли;
	
	Запрос.Текст = Запрос.Текст + ТекстЗапроса;
	
	Возврат Запрос;
	
КонецФункции

// Для функции ЗапросДанных.
Процедура УстановитьОтборВТекстЗапроса(ОписаниеПолей, ТекстЗапроса, Запрос, ПрефиксИмениПараметра)
	
	Отбор = "";
	СоединенияОтбора = "";
	
	Для Каждого ОписаниеПоля Из ОписаниеПолей Цикл
		ОчищаемыеТипы = Новый Соответствие;
		ОчищаемыеЗначения = Новый Соответствие;
		Для Каждого ОписаниеТипа Из ОписаниеПоля.Значение Цикл
			Если ТипЗнч(ОписаниеТипа.Значение) = Тип("Массив") Тогда
				ОчищаемыеЗначения.Вставить(ОписаниеТипа.Ключ, ОписаниеТипа.Значение);
			Иначе
				ОчищаемыеТипы.Вставить(ОписаниеТипа.Ключ, ОписаниеТипа.Значение);
			КонецЕсли;
		КонецЦикла;
		
		Если ОписаниеПоля.Значение.Количество() > 100
		   И ЗначениеЗаполнено(ОчищаемыеТипы) Тогда
			
			ИмяПараметра = ПрефиксИмениПараметра + ОписаниеПоля.Ключ;
			ТекстЗапросаВременнойТаблицы =
			"ВЫБРАТЬ
			|	ТекущаяТаблица.ПустаяСсылка КАК ПустаяСсылка
			|ПОМЕСТИТЬ ВременнаяТаблица
			|ИЗ
			|	&ВременнаяТаблица КАК ТекущаяТаблица
			|
			|ИНДЕКСИРОВАТЬ ПО
			|	ПустаяСсылка";
			ТекстЗапросаВременнойТаблицы = СтрЗаменить(ТекстЗапросаВременнойТаблицы,
				"ВременнаяТаблица", ИмяПараметра);
			Запрос.Текст = ТекстЗапросаВременнойТаблицы
				+ ОбщегоНазначения.РазделительПакетаЗапросов() + Запрос.Текст;
			СоединенияОтбора = СоединенияОтбора + ?(СоединенияОтбора = "", "", Символы.ПС);
			СоединенияОтбора = СоединенияОтбора + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			"		ЛЕВОЕ СОЕДИНЕНИЕ %1 КАК %1
			|		ПО (ТИПЗНАЧЕНИЯ(%1.ПустаяСсылка) = ТИПЗНАЧЕНИЯ(ТекущаяТаблица.%2))
			|", ИмяПараметра, ОписаниеПоля.Ключ);
			Отбор = Отбор + ?(Отбор = "", "", "
			|	ИЛИ ");
			Отбор = Отбор + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				"НЕ %1.ПустаяСсылка ЕСТЬ NULL", ИмяПараметра);
			ПустыеСсылки = Новый Массив;
			ТипыСсылок = Новый Массив;
			Для Каждого ОписаниеТипа Из ОчищаемыеТипы Цикл
				ПустыеСсылки.Добавить(ПредопределенноеЗначение(ОписаниеТипа.Значение + ".ПустаяСсылка"));
				ТипыСсылок.Добавить(ОписаниеТипа.Ключ);
			КонецЦикла;
			ЗначениеПараметра = Новый ТаблицаЗначений;
			ЗначениеПараметра.Колонки.Добавить("ПустаяСсылка", Новый ОписаниеТипов(ТипыСсылок));
			ЗначениеПараметра.ЗагрузитьКолонку(ПустыеСсылки, "ПустаяСсылка");
			Запрос.УстановитьПараметр(ИмяПараметра, ЗначениеПараметра);
		Иначе
			Для Каждого ОписаниеТипа Из ОчищаемыеТипы Цикл
				Отбор = Отбор + ?(Отбор = "", "", "
				|	ИЛИ ");
				Если СтрНайти(ОписаниеТипа.Значение, ".") > 0 Тогда
					Отбор = Отбор + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						"ТИПЗНАЧЕНИЯ(ТекущаяТаблица.%1) = ТИП(%2)",
						ОписаниеПоля.Ключ, ОписаниеТипа.Значение);
				Иначе
					ИмяПараметра = "Тип" + ОписаниеТипа.Значение;
					Запрос.УстановитьПараметр(ИмяПараметра, Тип(ОписаниеТипа.Значение));
					Отбор = Отбор + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						"ТИПЗНАЧЕНИЯ(ТекущаяТаблица.%1) = &%2",
						ОписаниеПоля.Ключ, ИмяПараметра);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Для Каждого ОписаниеТипа Из ОчищаемыеЗначения Цикл
			Отбор = Отбор + ?(Отбор = "", "", "
			|	ИЛИ ");
			Отбор = Отбор + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				"ТекущаяТаблица.%1 В (%2)",
				ОписаниеПоля.Ключ,
				"ЗНАЧЕНИЕ(" + СтрСоединить(ОписаниеТипа.Значение, "), ЗНАЧЕНИЕ(") + ")");
		КонецЦикла;
	КонецЦикла;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Отбор", Отбор);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ГДЕ", СоединенияОтбора + "ГДЕ");
	
КонецПроцедуры

// Для процедур УстаревшиеДанныеПриЗапросеПорцийВФоне,
// СформироватьСписокУстаревшихДанныхВФонеБезПопытки,
// функции ПланОчисткиУстаревшихДанных.
//
// Параметры:
//  ТолькоРегистры - Булево
//  УчестьРазделениеДанных - Булево
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Порядок       - Число
//   * ПолноеИмя     - Строка
//   * Представление - Строка
//   * ОчиститьВсе   - Булево
//   * ЭтоРегистр    - Булево
//   * Независимый   - Булево - независимый режим записи регистра.
//   * ПоляРегистра  - см. НовыеПоляРегистра
//   * ПоляСубконто  - см. НовыеПоляРегистра
//   * ЭтоПланОбмена - Булево
//   * ВПланеОбмена  - Булево - включен в состав хотя бы одного плана обмена.
//   * Неразделенный - Булево
//
Функция ОчищаемыеТаблицы(ТолькоРегистры, УчестьРазделениеДанных = Истина)
	
	Объекты = Новый Соответствие;
	ИнтеграцияПодсистемБСП.ПриЗаполненииОбъектовПланируемыхКУдалению(Объекты);
	ОбновлениеИнформационнойБазыПереопределяемый.ПриЗаполненииОбъектовПланируемыхКУдалению(Объекты);
	
	Результат = Новый ТаблицаЗначений;
	Результат.Колонки.Добавить("Порядок",       Новый ОписаниеТипов("Число"));
	Результат.Колонки.Добавить("ПолноеИмя",     Новый ОписаниеТипов("Строка"));
	Результат.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка"));
	Результат.Колонки.Добавить("ОчиститьВсе",   Новый ОписаниеТипов("Булево"));
	Результат.Колонки.Добавить("ЭтоРегистр",    Новый ОписаниеТипов("Булево"));
	Результат.Колонки.Добавить("Независимый",   Новый ОписаниеТипов("Булево"));
	Результат.Колонки.Добавить("ПоляРегистра",  Новый ОписаниеТипов("Соответствие"));
	Результат.Колонки.Добавить("ПоляСубконто",  Новый ОписаниеТипов("Соответствие"));
	Результат.Колонки.Добавить("ЭтоПланОбмена", Новый ОписаниеТипов("Булево"));
	Результат.Колонки.Добавить("ВПланеОбмена",  Новый ОписаниеТипов("Булево"));
	Результат.Колонки.Добавить("Неразделенный", Новый ОписаниеТипов("Булево"));
	Результат.Колонки.Добавить("Узлы");
	Результат.Колонки.Добавить("ПоследняяСсылка");
	
	ПорядокВидовОбъектов = ПорядокВидовОбъектов();
	РегистрацияИзменений = РегистрацияИзменений();
	ОпустошаемыеТаблицы  = Новый Соответствие;
	УдаляемыеТипы        = Новый Соответствие;
	УдаляемыеТипыПолей   = Новый Соответствие;
	
	ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ошибка в процедуре %1 общего модуля %2.'"),
		"ПриЗаполненииОбъектовПланируемыхКУдалению",
		"ОбновлениеИнформационнойБазыПереопределяемый")
		+ Символы.ПС + Символы.ПС;
	
	Для Каждого ОписаниеОбъекта Из Объекты Цикл
		ЧастиИмени = СтрРазделить(ОписаниеОбъекта.Ключ, ".");
		Если ЧастиИмени.Количество() = 2 Тогда
			ПолноеИмя = ОписаниеОбъекта.Ключ;
			ИмяПоля = Неопределено;
		Иначе
			ИмяПоля = ЧастиИмени[2];
			ПолноеИмя = ЧастиИмени[0] + "." + ЧастиИмени[1];
			ЧастиИмени.Удалить(0);
			ЧастиИмени.Удалить(0);
			ИмяПоля = СтрСоединить(ЧастиИмени, ".");
		КонецЕсли;
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя);
		Если ОбъектМетаданных = Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Объект метаданных ""%1"" не существует.'"), ПолноеИмя);
			ВызватьИсключение ТекстОшибки;
		ИначеЕсли ИмяПоля = Неопределено И Не СтрНачинаетсяС(ОбъектМетаданных.Имя, "Удалить") Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Имя объекта метаданных ""%1"" должно начинаться с ""%2"".'"), ПолноеИмя, "Удалить");
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
		ЭтоПеречисление = ОбщегоНазначения.ЭтоПеречисление(ОбъектМетаданных);
		ЭтоБизнесПроцесс = ОбщегоНазначения.ЭтоБизнесПроцесс(ОбъектМетаданных);
		Если ИмяПоля = Неопределено Или ЭтоПеречисление Или ЭтоБизнесПроцесс Тогда
			Если ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(ОбъектМетаданных) Или ЭтоПеречисление Тогда
				ТипСсылки = Тип(СтрЗаменить(ПолноеИмя, ".", "Ссылка."));
				Если ИмяПоля = Неопределено Тогда
					УдаляемыеТипы.Вставить(ТипСсылки, ПолноеИмя);
					Если ЭтоБизнесПроцесс Тогда
						ТипТочекМаршрута = ТипЗнч(ПредопределенноеЗначение(ПолноеИмя + ".ТочкаМаршрута.ПустаяСсылка"));
						УдаляемыеТипы.Вставить(ТипТочекМаршрута, ПолноеИмя + ".Точки");
					КонецЕсли;
				ИначеЕсли ЭтоПеречисление Тогда
					МетаданныеЗначения = ОбъектМетаданных.ЗначенияПеречисления.Найти(ИмяПоля); // ОбъектМетаданных
					Если МетаданныеЗначения = Неопределено Тогда
						ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Значение ""%1"" не существует.'"), ОписаниеОбъекта.Ключ);
						ВызватьИсключение ТекстОшибки;
					ИначеЕсли Не СтрНачинаетсяС(МетаданныеЗначения.Имя, "Удалить") Тогда
						ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Имя значения ""%1"" должно начинаться с ""%2"".'"), ОписаниеОбъекта.Ключ, "Удалить");
						ВызватьИсключение ТекстОшибки;
					КонецЕсли;
					ЗначенияПеречисления = УдаляемыеТипы.Получить(ТипСсылки);
					Если ЗначенияПеречисления = Неопределено Тогда
						ЗначенияПеречисления = Новый Соответствие;
						УдаляемыеТипы.Вставить(ТипСсылки, ЗначенияПеречисления);
					КонецЕсли;
					Если ТипЗнч(ЗначенияПеречисления) = Тип("Соответствие") Тогда
						ЗначенияПеречисления.Вставить(ПолноеИмя + "." + МетаданныеЗначения.Имя, Истина);
					КонецЕсли;
					Продолжить;
				ИначеЕсли ВРег(ИмяПоля) = ВРег("Точки") Тогда
					ТипТочекМаршрута = ТипЗнч(ПредопределенноеЗначение(ПолноеИмя + ".ТочкаМаршрута.ПустаяСсылка"));
					УдаляемыеТипы.Вставить(ТипТочекМаршрута, ПолноеИмя + ".Точки");
				Иначе // Точка маршрута бизнес-процесс.
					ЧастиПоля = СтрРазделить(ИмяПоля, ".", Истина);
					ТочкаМаршрута = Неопределено;
					Если ЧастиПоля.Количество() = 2 Тогда
						МенеджерБизнесПроцесса = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
						ИскомоеИмя = ВРег(ЧастиПоля[1]);
						Для Каждого ТекущаяТочкаМаршрута Из МенеджерБизнесПроцесса.ТочкиМаршрута Цикл
							Если ВРег(ТекущаяТочкаМаршрута.Имя) = ИскомоеИмя Тогда
								ТочкаМаршрута = ТекущаяТочкаМаршрута;
								Прервать;
							КонецЕсли;
						КонецЦикла;
					КонецЕсли;
					Если ТочкаМаршрута = Неопределено
					 Или ВРег(ЧастиПоля[0]) <> ВРег("ТочкаМаршрута") Тогда
						ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Значение ""%1"" не является существующей точкой маршрута.'"), ОписаниеОбъекта.Ключ);
						ВызватьИсключение ТекстОшибки;
					ИначеЕсли Не СтрНачинаетсяС(ТочкаМаршрута.Имя, "Удалить") Тогда
						ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Имя точки маршрута ""%1"" должно начинаться с ""%2"".'"), ОписаниеОбъекта.Ключ, "Удалить");
						ВызватьИсключение ТекстОшибки;
					КонецЕсли;
					ТипТочекМаршрута = ТипЗнч(ПредопределенноеЗначение(ПолноеИмя + ".ТочкаМаршрута.ПустаяСсылка"));
					ЗначенияТочекМаршрута = УдаляемыеТипы.Получить(ТипТочекМаршрута);
					Если ЗначенияТочекМаршрута = Неопределено Тогда
						ЗначенияТочекМаршрута = Новый Соответствие;
						УдаляемыеТипы.Вставить(ТипТочекМаршрута, ЗначенияТочекМаршрута);
					КонецЕсли;
					Если ТипЗнч(ЗначенияТочекМаршрута) = Тип("Соответствие") Тогда
						ЗначенияТочекМаршрута.Вставить(ПолноеИмя + ".ТочкаМаршрута." + ТочкаМаршрута.Имя, Истина);
					КонецЕсли;
					Продолжить;
				КонецЕсли;
			КонецЕсли;
			Если ОписаниеОбъекта.Значение = Истина И Не ЭтоПеречисление Тогда
				ЭтоРегистр = ОбщегоНазначения.ЭтоРегистр(ОбъектМетаданных);
				Если Не ТолькоРегистры Или ЭтоРегистр Тогда
					ОпустошаемыеТаблицы.Вставить(ПолноеИмя, Истина);
					НоваяСтрока = Результат.Добавить();
					НоваяСтрока.Порядок       = ПорядокВидовОбъектов.Получить(СтрРазделить(ПолноеИмя, ".")[0]);
					НоваяСтрока.ПолноеИмя     = ПолноеИмя;
					НоваяСтрока.Представление = ОбъектМетаданных.Представление();
					НоваяСтрока.ОчиститьВсе   = Истина;
					НоваяСтрока.ЭтоРегистр    = ЭтоРегистр;
					НоваяСтрока.Независимый   = ЭтоРегистр
						И ОбщегоНазначения.ЭтоРегистрСведений(ОбъектМетаданных)
						И ОбъектМетаданных.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый;
					НоваяСтрока.ЭтоПланОбмена = ОбщегоНазначения.ЭтоПланОбмена(ОбъектМетаданных);
					НоваяСтрока.ВПланеОбмена  = РегистрацияИзменений.Получить(ОбъектМетаданных) <> Неопределено;
					НоваяСтрока.Неразделенный = ЭтоНеразделенныйОбъект(ОбъектМетаданных);
				КонецЕсли;
			КонецЕсли;
		Иначе
			УдаляемыеТипыПолей.Вставить(ПолноеИмя + "." + ИмяПоля, ОписаниеОбъекта.Значение);
		КонецЕсли;
	КонецЦикла;
	
	ВидыРегистровДляОчистки = ВидыРегистровДляОчистки();
	УточнитьВидыРегистровДляОчистки(ВидыРегистровДляОчистки);
	
	Контекст = Новый Структура;
	Контекст.Вставить("ОчищаемыеТаблицы",        Результат);
	Контекст.Вставить("ЗаголовокОшибки",         ЗаголовокОшибки);
	Контекст.Вставить("УдаляемыеТипы",           УдаляемыеТипы);
	Контекст.Вставить("УдаляемыеТипыПолей",      УдаляемыеТипыПолей);
	Контекст.Вставить("ИменаТаблицПоТипам",      ИменаТаблицПоТипам());
	Контекст.Вставить("ОпустошаемыеТаблицы",     ОпустошаемыеТаблицы);
	Контекст.Вставить("ПорядокВидовОбъектов",    ПорядокВидовОбъектов);
	Контекст.Вставить("РегистрацияИзменений",    РегистрацияИзменений);
	Контекст.Вставить("ВидыРегистровДляОчистки", ВидыРегистровДляОчистки);
	Контекст.Вставить("ИменаПростыхТипов",       ИменаПростыхТиповПолейТаблиц());
	
	ДобавитьУдаляемыеТипыПолейРегистров(Контекст, "РегистрыСведений");
	ДобавитьУдаляемыеТипыПолейРегистров(Контекст, "РегистрыНакопления");
	ДобавитьУдаляемыеТипыПолейРегистров(Контекст, "РегистрыБухгалтерии");
	ДобавитьУдаляемыеТипыПолейРегистров(Контекст, "РегистрыРасчета");
	
	ИзбыточныеТипыПолей = Новый Массив;
	Для Каждого УдаляемыеТипыПоля Из УдаляемыеТипыПолей Цикл
		ИменаТиповПоля = Новый Массив;
		Для Каждого КлючИЗначение Из УдаляемыеТипыПоля.Значение Цикл
			ИменаТиповПоля.Добавить(ОбщегоНазначения.СтроковоеПредставлениеТипа(КлючИЗначение.Ключ));
		КонецЦикла;
		ИзбыточныеТипыПолей.Добавить(УдаляемыеТипыПоля.Ключ + " (" + СтрСоединить(ИменаТиповПоля, ", ") + ")");
	КонецЦикла;
	Если ЗначениеЗаполнено(ИзбыточныеТипыПолей) Тогда
		ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Следующие типы полей не могут применяться для очистки устаревших данных:
			           |%1'"),
			"- " + СтрСоединить(ИзбыточныеТипыПолей, ";" + Символы.ПС + "- ") + ".");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если УчестьРазделениеДанных И ОбщегоНазначения.РазделениеВключено() Тогда
		Отбор = Новый Структура("Неразделенный", Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных());
		Результат = Результат.Скопировать(Результат.НайтиСтроки(Отбор));
	КонецЕсли;
	
	Результат.Сортировать("Порядок, ПолноеИмя");
	
	Возврат Результат;
	
КонецФункции

// Для функции ОчищаемыеТаблицы.
Функция РегистрацияИзменений()
	
	Результат = Новый Соответствие;
	
	Для Каждого ПланОбмена Из Метаданные.ПланыОбмена Цикл
		Для Каждого ЭлементСостава Из ПланОбмена.Состав Цикл
			Результат.Вставить(ЭлементСостава.Метаданные, Истина);
		КонецЦикла;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

// Для функции ОчищаемыеТаблицы.
Функция ПорядокВидовОбъектов()
	
	Результат = Новый Соответствие;
	Результат.Вставить("ПланОбмена", 1);
	Результат.Вставить("Справочник", 2);
	Результат.Вставить("Документ", 3);
	Результат.Вставить("Перечисление", 4);
	Результат.Вставить("ПланВидовХарактеристик", 5);
	Результат.Вставить("ПланСчетов", 6);
	Результат.Вставить("ПланВидовРасчета", 7);
	Результат.Вставить("РегистрСведений", 8);
	Результат.Вставить("РегистрНакопления", 9);
	Результат.Вставить("РегистрБухгалтерии", 10);
	Результат.Вставить("РегистрРасчета", 11);
	Результат.Вставить("БизнесПроцесс", 12);
	Результат.Вставить("Задача", 13);
	
	Возврат Результат;
	
КонецФункции

// Возвращает какие из видов регистров будут очищаться от
// ссылочных данных с именами Удалить*.
//
// Регистры, кроме регистров сведений, во многих случаях
// проходят реструктуризацию успешно при сокращении типов
// в измерениях и значениях субконто.
//
// Возвращаемое значение:
//  Структура:
//   * РегистрыСведений - Булево
//   * РегистрыНакопления - Булево
//   * РегистрыБухгалтерии - Булево
//   * РегистрыБухгалтерииСубконто - Булево
//   * РегистрыРасчета - Булево
//
Функция ВидыРегистровДляОчистки()
	
	Результат = Новый Структура;
	Результат.Вставить("РегистрыСведений", Истина);
	Результат.Вставить("РегистрыНакопления", Ложь);
	Результат.Вставить("РегистрыБухгалтерии", Ложь);
	Результат.Вставить("РегистрыБухгалтерииСубконто", Ложь);
	Результат.Вставить("РегистрыРасчета", Ложь);
	
	Возврат Результат;
	
КонецФункции

Функция ИменаТаблицПоТипам()
	
	Результат = Новый Соответствие;
	Для Каждого МетаданныеБизнесПроцесса Из Метаданные.БизнесПроцессы Цикл
		ПолноеИмя = МетаданныеБизнесПроцесса.ПолноеИмя();
		ТипТочекМаршрута = ТипЗнч(ПредопределенноеЗначение(ПолноеИмя + ".ТочкаМаршрута.ПустаяСсылка"));
		Результат.Вставить(ТипТочекМаршрута, ПолноеИмя + ".Точки");
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция ИменаПростыхТиповПолейТаблиц();
	
	Имена = Новый Соответствие;
	Имена.Вставить(Тип("Число"), "Число");
	Имена.Вставить(Тип("Строка"), "Строка");
	Имена.Вставить(Тип("Дата"), "Дата");
	Имена.Вставить(Тип("Булево"), "Булево");
	Имена.Вставить(Тип("ХранилищеЗначения"), "ХранилищеЗначения");
	Имена.Вставить(Тип("УникальныйИдентификатор"), "УникальныйИдентификатор");
	
	Возврат Имена;
	
КонецФункции

// Для переопределения с помощью расширения при необходимости.
//
// Параметры:
//  ВидыОчистки - см. ВидыРегистровДляОчистки
//
Процедура УточнитьВидыРегистровДляОчистки(ВидыОчистки)
	Возврат;
КонецПроцедуры

// Для функции ОчищаемыеТаблицы и ДобавитьУдаляемыеТипыПолейРегистров.
Функция ЭтоНеразделенныйОбъект(ОбъектМетаданных)
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Разделенный = Ложь;
	Если ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
		Разделенный = МодульРаботаВМоделиСервиса.ЭтоРазделенныйОбъектМетаданных(ОбъектМетаданных);
	КонецЕсли;
	
	Возврат Не Разделенный;
	
КонецФункции

// Для функции ОчищаемыеТаблицы.
Процедура ДобавитьУдаляемыеТипыПолейРегистров(Контекст, ВидРегистров)
	
	Регистры = Метаданные[ВидРегистров]; // КоллекцияОбъектовМетаданных
	
	Для Каждого Регистр Из Регистры Цикл
		ПолноеИмя = Регистр.ПолноеИмя();
		ПоляРегистра = НовыеПоляРегистра();
		ПоляСубконто = НовыеПоляРегистра();
		Если Регистры <> Метаданные.РегистрыСведений
		 Или Регистр.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.ПодчинениеРегистратору Тогда
			Для Каждого СтандартныйРеквизит Из Регистр.СтандартныеРеквизиты Цикл
				СтандартныйРеквизит = СтандартныйРеквизит; // ОписаниеСтандартногоРеквизита
				Если СтандартныйРеквизит.Имя = "Регистратор" Тогда
					ДобавитьУдаляемыеТипыПоля(ПоляРегистра, СтандартныйРеквизит, ПолноеИмя, Контекст, ВидРегистров);
				КонецЕсли;
			КонецЦикла;
			Независимый = Ложь;
		Иначе
			Независимый = Истина;
		КонецЕсли;
		Для Каждого Измерение Из Регистр.Измерения Цикл
			Если Регистры = Метаданные.РегистрыБухгалтерии
			   И Не Измерение.Балансовый
			   И Регистр.ПланСчетов <> Неопределено
			   И Регистр.Корреспонденция Тогда
				Поле = Новый Структура("Имя, Тип",, Измерение.Тип);
				Поле.Имя = Измерение.Имя + "Дт";
				ДобавитьУдаляемыеТипыПоля(ПоляРегистра, Поле, ПолноеИмя, Контекст, ВидРегистров);
				Поле.Имя = Измерение.Имя + "Кт";
				ДобавитьУдаляемыеТипыПоля(ПоляРегистра, Поле, ПолноеИмя, Контекст, ВидРегистров);
				Продолжить;
			КонецЕсли;
			ДобавитьУдаляемыеТипыПоля(ПоляРегистра, Измерение, ПолноеИмя, Контекст, ВидРегистров);
		КонецЦикла;
		Если Регистры = Метаданные.РегистрыБухгалтерии
		   И Регистр.ПланСчетов <> Неопределено Тогда
			ИмяТипаСсылкиПланаСчетов = СтрЗаменить(Регистр.ПланСчетов.ПолноеИмя(), ".", "Ссылка.");
			Поле = Новый Структура("Имя, Тип", "Счет", Новый ОписаниеТипов(ИмяТипаСсылкиПланаСчетов));
			Если Регистр.Корреспонденция Тогда
				Поле.Имя = "СчетДт";
				ДобавитьУдаляемыеТипыПоля(ПоляРегистра, Поле, ПолноеИмя, Контекст, ВидРегистров);
				Поле.Имя = "СчетКт";
				ДобавитьУдаляемыеТипыПоля(ПоляРегистра, Поле, ПолноеИмя, Контекст, ВидРегистров);
			Иначе
				ДобавитьУдаляемыеТипыПоля(ПоляРегистра, Поле, ПолноеИмя, Контекст, ВидРегистров);
			КонецЕсли;
			Если Регистр.ПланСчетов.МаксКоличествоСубконто > 0
			   И Регистр.ПланСчетов.ВидыСубконто <> Неопределено Тогда
				ИмяТипаСсылкиВидаСубконто = СтрЗаменить(Регистр.ПланСчетов.ВидыСубконто.ПолноеИмя(), ".", "Ссылка.");
				Поле = Новый Структура("Имя, Тип", "Вид", Новый ОписаниеТипов(ИмяТипаСсылкиВидаСубконто));
				ДобавитьУдаляемыеТипыПоля(ПоляСубконто, Поле, ПолноеИмя, Контекст, "РегистрыБухгалтерииСубконто");
				Поле = Новый Структура("Имя, Тип", "Значение", Регистр.ПланСчетов.ВидыСубконто.Тип);
				ДобавитьУдаляемыеТипыПоля(ПоляСубконто, Поле, ПолноеИмя, Контекст, "РегистрыБухгалтерииСубконто");
			КонецЕсли;
		КонецЕсли;
		Если Контекст.ОпустошаемыеТаблицы.Получить(ПолноеИмя) = Неопределено
		   И (ЗначениеЗаполнено(ПоляРегистра)
		      Или ЗначениеЗаполнено(ПоляСубконто)) Тогда
			НоваяСтрока = Контекст.ОчищаемыеТаблицы.Добавить();
			НоваяСтрока.Порядок       = Контекст.ПорядокВидовОбъектов.Получить(СтрРазделить(ПолноеИмя, ".")[0]);
			НоваяСтрока.ПолноеИмя     = ПолноеИмя;
			НоваяСтрока.Представление = Регистр.Представление();
			НоваяСтрока.ЭтоРегистр    = Истина;
			НоваяСтрока.Независимый   = Независимый;
			НоваяСтрока.ПоляРегистра  = ПоляРегистра;
			НоваяСтрока.ПоляСубконто  = ПоляСубконто;
			НоваяСтрока.ВПланеОбмена  = Контекст.РегистрацияИзменений.Получить(Регистр) <> Неопределено;
			НоваяСтрока.Неразделенный = ЭтоНеразделенныйОбъект(Регистр);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьУдаляемыеТипыПолейРегистров.
//
// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ     - имя поля.
//   * Значение - Соответствие из КлючИЗначение:
//      ** Ключ     - Тип - тип ссылки.
//      ** Значение - Строка - полное имя типа для запроса.
//                  - Массив из Строка - полные имена значений для запроса.
//
Функция НовыеПоляРегистра()
	
	Возврат Новый Соответствие;
	
КонецФункции

// Для процедуры ДобавитьУдаляемыеТипыПолейРегистров.
Процедура ДобавитьУдаляемыеТипыПоля(Поля, Поле, ПолноеИмяРегистра, Контекст, ВидРегистров)
	
	ВсеУдаляемыеТипыПоля = Поля.Получить(Поле.Имя);
	Если ВсеУдаляемыеТипыПоля = Неопределено Тогда
		ВсеУдаляемыеТипыПоля = Новый Соответствие;
	КонецЕсли;
	ТипыПоля = Поле.Тип;
	
	Если Контекст.ВидыРегистровДляОчистки[ВидРегистров] Тогда
		УдаляемыеТипы = Контекст.УдаляемыеТипы;
		Для Каждого Тип Из ТипыПоля.Типы() Цикл
			ИмяТаблицы = УдаляемыеТипы.Получить(Тип);
			Если ИмяТаблицы = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Если ТипЗнч(ИмяТаблицы) = Тип("Строка") Тогда
				ВсеУдаляемыеТипыПоля.Вставить(Тип, ИмяТаблицы);
			Иначе
				ДобавитьЗначенияПеречисления(ВсеУдаляемыеТипыПоля, Тип, ИмяТаблицы);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ПолноеИмяПоля = ПолноеИмяРегистра + "." + Поле.Имя;
	УдаляемыеТипыПоля = Контекст.УдаляемыеТипыПолей.Получить(ПолноеИмяПоля);
	Если УдаляемыеТипыПоля <> Неопределено Тогда
		ИменаТаблицПоТипам = Контекст.ИменаТаблицПоТипам;
		Для Каждого КлючИЗначение Из УдаляемыеТипыПоля Цикл
			ТипИлиЗначение = КлючИЗначение.Ключ;
			Если ТипЗнч(ТипИлиЗначение) = Тип("Тип") Тогда
				Тип = ТипИлиЗначение;
			Иначе
				Тип = ТипЗнч(ТипИлиЗначение);
			КонецЕсли;
			Если Не ТипыПоля.СодержитТип(Тип) Тогда
				ТекстОшибки = Контекст.ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Поле ""%1"" не содержит типа ""%2"".'"),
					ПолноеИмяПоля, ОбщегоНазначения.СтроковоеПредставлениеТипа(Тип));
				ВызватьИсключение ТекстОшибки;
			Иначе
				ИмяТаблицы = ИменаТаблицПоТипам.Получить(Тип);
				Если ИмяТаблицы = Неопределено Тогда
					ИмяТаблицы = Контекст.ИменаПростыхТипов.Получить(Тип);
					Если ИмяТаблицы = Неопределено Тогда
						МетаданныеТаблицы = Метаданные.НайтиПоТипу(Тип);
						Если МетаданныеТаблицы = Неопределено Тогда
							ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
								НСтр("ru = 'Не удалость найти объект метаданных по типу ""%1"" сокращаемый в поле ""%2"".'"),
								Строка(Тип), ПолноеИмяПоля);
							ЗаписьЖурналаРегистрации(
								НСтр("ru = 'Очистка устаревших данных.Ошибка при подготовке списка очищаемых таблиц'",
									ОбщегоНазначения.КодОсновногоЯзыка()),
								УровеньЖурналаРегистрации.Ошибка,,, ТекстОшибки);
							Продолжить;
						Иначе
							ИмяТаблицы = МетаданныеТаблицы.ПолноеИмя();
						КонецЕсли;
					КонецЕсли;
					ИменаТаблицПоТипам.Вставить(Тип, ИмяТаблицы);
				КонецЕсли;
				Если Тип = ТипИлиЗначение Тогда
					ВсеУдаляемыеТипыПоля.Вставить(Тип, ИмяТаблицы);
				Иначе
					ЭтоЗначениеПеречисления = Перечисления.ТипВсеСсылки().СодержитТип(Тип);
					Если Не ЭтоЗначениеПеречисления
					   И Не БизнесПроцессы.ТипВсеСсылкиТочекМаршрутаБизнесПроцессов().СодержитТип(Тип) Тогда
						ТекстОшибки = Контекст.ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Для поля ""%1""
							           |указано значение типа ""%2"",
							           |которое не является значением перечисления
							           |или точкой маршрута бизнес-процесса.'"),
							ПолноеИмяПоля,
							ОбщегоНазначения.СтроковоеПредставлениеТипа(Тип));
						ВызватьИсключение ТекстОшибки;
					КонецЕсли;
					УдаляемыеЗначения = Новый Соответствие;
					Если ЭтоЗначениеПеречисления Тогда
						УдаляемыеЗначения.Вставить(ИмяТаблицы + "." + XMLСтрока(ТипИлиЗначение), Истина);
					Иначе
						ТочкаМаршрута = ТипИлиЗначение; // ТочкаМаршрутаБизнесПроцессаСсылкаИмяБизнесПроцесса
						ЧастиИмени = СтрРазделить(ИмяТаблицы, ".");
						ЧастиИмени[2] = "ТочкаМаршрута";
						УдаляемыеЗначения.Вставить(СтрСоединить(ЧастиИмени, ".") + "." + ТочкаМаршрута.Имя, Истина);
					КонецЕсли;
					ДобавитьЗначенияПеречисления(ВсеУдаляемыеТипыПоля, Тип, УдаляемыеЗначения);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Контекст.УдаляемыеТипыПолей.Удалить(ПолноеИмяПоля);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ВсеУдаляемыеТипыПоля) Тогда
		Поля.Вставить(Поле.Имя, ВсеУдаляемыеТипыПоля);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ДобавитьУдаляемыеТипыПоля.
Процедура ДобавитьЗначенияПеречисления(ВсеУдаляемыеТипыПоля, Тип, УдаляемыеЗначения)
	
	ВсеЗначения = ВсеУдаляемыеТипыПоля.Получить(Тип);
	Если ТипЗнч(ВсеЗначения) = Тип("Строка") Тогда
		Возврат;
	КонецЕсли;
	
	Если ВсеЗначения = Неопределено Тогда
		ВсеЗначения = Новый Массив;
		ВсеУдаляемыеТипыПоля.Вставить(Тип, ВсеЗначения);
	КонецЕсли;
	
	Для Каждого КлючИЗначение Из УдаляемыеЗначения Цикл
		Если ВсеЗначения.Найти(КлючИЗначение.Ключ) = Неопределено Тогда
			ВсеЗначения.Добавить(КлючИЗначение.Ключ);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// ВСПОМОГАТЕЛЬНЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ

////////////////////////////////////////////////////////////////////////////////
// Общего назначения

Процедура УстановитьПорядокОтложенногоОбновления() Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(Метаданные.РегламентныеЗадания.УстановкаПорядкаОтложенногоОбновленияВМоделиСервиса);
	
	Если Не ОбщегоНазначения.РазделениеВключено()
		Или ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		// Регламентное задание предназначено для запуска только в неразделенном режиме работы.
		Задания = РегламентныеЗаданияСервер.НайтиЗадания(Новый Структура("Метаданные", Метаданные.РегламентныеЗадания.УстановкаПорядкаОтложенногоОбновленияВМоделиСервиса));
		Для Каждого Задание Из Задания Цикл
			РегламентныеЗаданияСервер.ИзменитьЗадание(Задание.УникальныйИдентификатор,
				Новый Структура("Использование", Ложь));
		КонецЦикла;
		Возврат;
	КонецЕсли;
	
	ПрогрессОбновления = ОбновлениеИнформационнойБазы.ПрогрессОбновленияОбластейДанных("Отложенное");
	ОбластиДляПроверки = ПрогрессОбновления.ОбластиВыполняется;
	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ОбластиДляПроверки, ПрогрессОбновления.ОбластиСПроблемами, Истина);
	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ОбластиДляПроверки, ПрогрессОбновления.ОбластиОжидает, Истина);
	
	ПорядокОбрабатываемыхДанных = Константы.ПорядокОбрабатываемыхДанных.Получить();
	
	Статусы = Новый Массив;
	Статусы.Добавить(Перечисления.СтатусыОбработчиковОбновления.НеВыполнялся);
	Статусы.Добавить(Перечисления.СтатусыОбработчиковОбновления.Выполняется);
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ОбластиДляПроверки", ОбластиДляПроверки);
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.УстановитьПараметр("Порядок", Перечисления.ПорядокОбработчиковОбновления.Критичный);
	Запрос.УстановитьПараметр("Статусы", Статусы);
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|	И ОбработчикиОбновления.ОбластьДанныхВспомогательныеДанные В(&ОбластиДляПроверки)
		|	И ОбработчикиОбновления.Порядок = &Порядок
		|	И ОбработчикиОбновления.Статус В(&Статусы)
		|	И &УсловиеОбычныхОбработчиков";
	
	УсловиеОбычныхОбработчиков = "ИСТИНА";
	Если ПорядокОбрабатываемыхДанных = Перечисления.ПорядокОбработчиковОбновления.Обычный Тогда
		// АПК:1297-выкл Условие запроса.
		УсловиеОбычныхОбработчиков = "(Не ОбработчикиОбновления.ИспользуетсяРазделениеПоКритичности
			|	Или Не ОбработчикиОбновления.ОбработаныАктуальныеДанные)";
		// АПК:1297-вкл
	КонецЕсли;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОбычныхОбработчиков", УсловиеОбычныхОбработчиков);
	
	ОтключитьЗадание = Ложь;
	Если Запрос.Выполнить().Пустой() Тогда
		Если ПорядокОбрабатываемыхДанных = Перечисления.ПорядокОбработчиковОбновления.Критичный Тогда
			Константы.ПорядокОбрабатываемыхДанных.Установить(Перечисления.ПорядокОбработчиковОбновления.Обычный);
		Иначе
			Константы.ПорядокОбрабатываемыхДанных.Установить(Перечисления.ПорядокОбработчиковОбновления.Некритичный);
			ОтключитьЗадание = Истина;
		КонецЕсли;
	КонецЕсли;
	
	Если ОтключитьЗадание Тогда
		РегламентныеЗаданияСервер.УстановитьИспользованиеРегламентногоЗадания(Метаданные.РегламентныеЗадания.УстановкаПорядкаОтложенногоОбновленияВМоделиСервиса, Ложь);
	КонецЕсли;
	
КонецПроцедуры

Процедура ОтключитьОбновлениеКлючейДоступа(Значение, ПодсистемаСуществует)
	Если ПодсистемаСуществует Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Значение);
	КонецЕсли;
КонецПроцедуры

Функция РежимОбновленияДанныхВЛокальномРежимеРаботы()
	
	УстановитьПривилегированныйРежим(Истина);
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	1 КАК ЕстьВерсииПодсистем
		|ИЗ
		|	РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем";
	
	РезультатВыполненияПакета = Запрос.ВыполнитьПакет();
	Если РезультатВыполненияПакета[0].Пустой() Тогда
		Возврат "НачальноеЗаполнение";
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	1 КАК ЕстьВерсииПодсистем
		|ИЗ
		|	РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем
		|ГДЕ
		|	ВерсииПодсистем.ЭтоОсновнаяКонфигурация = ИСТИНА
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	1 КАК ЕстьВерсииПодсистем
		|ИЗ
		|	РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем
		|ГДЕ
		|	ВерсииПодсистем.ИмяПодсистемы = &ИмяОсновнойКонфигурации
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	1 КАК ЕстьВерсииПодсистем
		|ИЗ
		|	РегистрСведений.ВерсииПодсистем КАК ВерсииПодсистем
		|ГДЕ
		|	ВерсииПодсистем.ЭтоОсновнаяКонфигурация = ИСТИНА
		|	И ВерсииПодсистем.ИмяПодсистемы = &ИмяОсновнойКонфигурации";
	Запрос.УстановитьПараметр("ИмяОсновнойКонфигурации", Метаданные.Имя);
	РезультатВыполненияПакета = Запрос.ВыполнитьПакет();
	Если РезультатВыполненияПакета[0].Пустой() И Не РезультатВыполненияПакета[1].Пустой() Тогда
		Возврат "ОбновлениеВерсии"; // Признак ЭтоОсновнаяКонфигурация еще не был заполнен.
	КонецЕсли;
	
	// Определяем по ранее заполненному признаку ЭтоОсновнаяКонфигурация.
	Возврат ?(РезультатВыполненияПакета[2].Пустой(), "ПереходСДругойПрограммы", "ОбновлениеВерсии");
	
КонецФункции	

// АПК:581-выкл используется для тестирования.
Функция ВозможноОперативноеОбновление(ПроверяемыеИтерацииОбновления = Неопределено) Экспорт
	
	Если ПроверяемыеИтерацииОбновления = Неопределено Тогда
		// Режим вызова для определения полного состава процедур обработчиков обновления,
		// требующих монопольный режим (без записи сообщений в журнал регистрации).
		ИтерацииОбновления = ИтерацииОбновления();
	Иначе
		ИтерацииОбновления = ПроверяемыеИтерацииОбновления;
	КонецЕсли;
	
	ФильтрыРазделенностиОбработчиков = Новый Массив;
	Если НЕ ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		ФильтрыРазделенностиОбработчиков.Добавить(Ложь);
	КонецЕсли;
	ФильтрыРазделенностиОбработчиков.Добавить(Истина);
	
	// В режиме проверки параметр не используется.
	ОбязательныеРазделенныеОбработчики = ОбновлениеИнформационнойБазы.НоваяТаблицаОбработчиковОбновления();
	
	ЗаписыватьВЖурнал = Константы.ДетализироватьОбновлениеИБВЖурналеРегистрации.Получить();
	ПроцедурыОбработчиков = Новый Массив;
	
	// Проверяем обработчики обновления с флагом МонопольныйРежим для подсистем конфигурации.
	Для каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
		
		ПараметрыОтбора = ПараметрыОтбораОбработчиков();
		ПараметрыОтбора.РежимОбновления = "Оперативно";
		
		Для каждого ФлагРазделенности Из ФильтрыРазделенностиОбработчиков Цикл
		
			ПараметрыОтбора.ПолучатьРазделенные = ФлагРазделенности;
			
			ДеревоОбработчиков = ОбработчикиОбновленияВИнтервале(ИтерацияОбновления.Обработчики, ИтерацияОбновления.ПредыдущаяВерсия,
				ИтерацияОбновления.Версия, ПараметрыОтбора);
			Если ДеревоОбработчиков.Строки.Количество() = 0 Тогда
				Продолжить;
			КонецЕсли;
				
			Если ДеревоОбработчиков.Строки.Количество() > 1 
				ИЛИ ДеревоОбработчиков.Строки[0].Версия <> "*" Тогда
				Для Каждого СтрокаВерсия Из ДеревоОбработчиков.Строки Цикл
					Если СтрокаВерсия.Версия = "*" Тогда
						Продолжить;
					КонецЕсли;
					Для Каждого Обработчик Из СтрокаВерсия.Строки Цикл
						ПроцедурыОбработчиков.Добавить(Обработчик.Процедура);
					КонецЦикла;
				КонецЦикла;
			КонецЕсли;
			
			Если ФлагРазделенности 
				И ОбщегоНазначения.РазделениеВключено() 
				И НЕ ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
				
				// При обновлении неразделенной версии ИБ, для разделенных обязательных
				// обработчиков обновления монопольным режимом управляет неразделенный обработчик.
				Продолжить;
			КонецЕсли;
			
			НайденныеОбработчики = ДеревоОбработчиков.Строки[0].Строки.НайтиСтроки(Новый Структура("МонопольныйРежим", Неопределено));
			Для Каждого Обработчик Из НайденныеОбработчики Цикл
				ПроцедурыОбработчиков.Добавить(Обработчик.Процедура);
			КонецЦикла;
			
			// Вызов обязательных обработчиков обновления в режиме проверки.
			Для каждого Обработчик Из ДеревоОбработчиков.Строки[0].Строки Цикл
				Если Обработчик.ВерсияРегистрации <> "*" Тогда
					ПроцедурыОбработчиков.Добавить(Обработчик.Процедура);
					Продолжить;
				КонецЕсли;
				
				ПараметрыОбработчика = Новый Структура;
				Если Обработчик.УправлениеОбработчиками Тогда
					ПараметрыОбработчика.Вставить("РазделенныеОбработчики", ОбязательныеРазделенныеОбработчики);
				КонецЕсли;
				ПараметрыОбработчика.Вставить("МонопольныйРежим", Ложь);
				
				ДополнительныеПараметры = Новый Структура;
				ДополнительныеПараметры.Вставить("ЗаписыватьВЖурнал", ЗаписыватьВЖурнал);
				ДополнительныеПараметры.Вставить("ИдентификаторБиблиотеки", ИтерацияОбновления.Подсистема);
				ДополнительныеПараметры.Вставить("ХодВыполненияОбработчиков", Неопределено);
				ДополнительныеПараметры.Вставить("ВФоне", Ложь);
				
				ВыполнитьОбработчикОбновления(Обработчик, ПараметрыОбработчика, ДополнительныеПараметры);
				
				Если ПараметрыОбработчика.МонопольныйРежим = Истина Тогда
					ПроцедурыОбработчиков.Добавить(Обработчик.Процедура);
				КонецЕсли;
			КонецЦикла;
			
		КонецЦикла;
	КонецЦикла;
	
	Если ПроверяемыеИтерацииОбновления = Неопределено Тогда
		ПроверяемыеИтерацииОбновления = ПроцедурыОбработчиков;
		Возврат ПроцедурыОбработчиков.Количество() = 0;
	КонецЕсли;
	
	Если ПроцедурыОбработчиков.Количество() <> 0 Тогда
		ТекстСообщения = НСтр("ru = 'Следующие обработчики не поддерживают обновление без установки монопольного режима:'");
		ТекстСообщения = ТекстСообщения + Символы.ПС;
		Для Каждого ПроцедураОбработчика Из ПроцедурыОбработчиков Цикл
			ТекстСообщения = ТекстСообщения + Символы.ПС + ПроцедураОбработчика;
		КонецЦикла;
		ЗаписатьОшибку(ТекстСообщения);
	КонецЕсли;
	
	Возврат ПроцедурыОбработчиков.Количество() = 0;
	
КонецФункции
// АПК:581-вкл.

Процедура СкопироватьСтрокиВДерево(Знач СтрокиПриемника, Знач СтрокиИсточника, Знач СтруктураКолонок)
	
	Для каждого СтрокаИсточника Из СтрокиИсточника Цикл
		ЗаполнитьЗначенияСвойств(СтруктураКолонок, СтрокаИсточника);
		НайденныеСтроки = СтрокиПриемника.НайтиСтроки(СтруктураКолонок);
		Если НайденныеСтроки.Количество() = 0 Тогда
			СтрокаПриемника = СтрокиПриемника.Добавить();
			ЗаполнитьЗначенияСвойств(СтрокаПриемника, СтрокаИсточника);
		Иначе
			СтрокаПриемника = НайденныеСтроки[0];
		КонецЕсли;
		
		СкопироватьСтрокиВДерево(СтрокаПриемника.Строки, СтрокаИсточника.Строки, СтруктураКолонок);
	КонецЦикла;
	
КонецПроцедуры

Функция ПолучитьПланОбновления(Знач ИдентификаторБиблиотеки, Знач ВерсияС, Знач ВерсияНа)
	
	МенеджерЗаписи = РегистрыСведений.ВерсииПодсистем.СоздатьМенеджерЗаписи();
	МенеджерЗаписи.ИмяПодсистемы = ИдентификаторБиблиотеки;
	МенеджерЗаписи.Прочитать();
	Если НЕ МенеджерЗаписи.Выбран() Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	ОписаниеПлана = МенеджерЗаписи.ПланОбновления.Получить();
	Если ОписаниеПлана = Неопределено Тогда
		
		Возврат Неопределено;
		
	Иначе
		
		Если ОписаниеПлана.ВерсияС <> ВерсияС
			ИЛИ ОписаниеПлана.ВерсияНа <> ВерсияНа Тогда
			
			// План устарел и не соответствует текущей версии.
			Возврат Неопределено;
		КонецЕсли;
		
		Возврат ОписаниеПлана.План;
		
	КонецЕсли;
	
КонецФункции

Процедура ВыполнитьОбработчикОбновления(Обработчик, Параметры, ДополнительныеПараметры)
	
	ЗаписатьИнформациюОХодеОбновления(Обработчик, ДополнительныеПараметры.ХодВыполненияОбработчиков, ДополнительныеПараметры.ВФоне);
	ОписаниеОбработчика = 
		ПодготовитьДетальнуюИнформациюОХодеОбновления(Обработчик, Параметры, ДополнительныеПараметры.ИдентификаторБиблиотеки);
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОценкаПроизводительности") Тогда
		МодульОценкаПроизводительности = ОбщегоНазначения.ОбщийМодуль("ОценкаПроизводительности");
		НачалоЗамера = МодульОценкаПроизводительности.НачатьЗамерВремени();
	КонецЕсли;
	
	Если Параметры <> Неопределено Тогда
		ПараметрыОбработчика = Новый Массив;
		ПараметрыОбработчика.Добавить(Параметры);
	Иначе
		ПараметрыОбработчика = Неопределено;
	КонецЕсли;
	
	ТранзакцияАктивнаНаНачалоВыполнения = ТранзакцияАктивна();
	
	ПодсистемаСуществует = ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом");
	ОтключитьОбновлениеКлючейДоступа(Истина, ПодсистемаСуществует);
	Попытка
		УстановитьПараметрыОбработчикаОбновления(Обработчик);
		УстановитьСтатусОбработчика(Обработчик.Процедура, "Выполняется");
		
		НачалоОбработки = ТекущаяУниверсальнаяДатаВМиллисекундах();
		ОбщегоНазначения.ВыполнитьМетодКонфигурации(Обработчик.Процедура, ПараметрыОбработчика);
		КонецОбработки = ТекущаяУниверсальнаяДатаВМиллисекундах();
		
		УстановитьПараметрыОбработчикаОбновления(Неопределено);
		
		ПроверитьВложеннуюТранзакцию(ТранзакцияАктивнаНаНачалоВыполнения, Обработчик.Процедура);
		
		УстанавливаемыеСвойства = Новый Структура;
		УстанавливаемыеСвойства.Вставить("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
		УстанавливаемыеСвойства.Вставить("ДлительностьОбработки", КонецОбработки - НачалоОбработки);
		УстановитьСвойстваОбработчика(Обработчик.Процедура, УстанавливаемыеСвойства);
		
		ОтключитьОбновлениеКлючейДоступа(Ложь, ПодсистемаСуществует);
	Исключение
		ПроверитьВложеннуюТранзакцию(ТранзакцияАктивнаНаНачалоВыполнения, Обработчик.Процедура);
		
		ОтключитьОбновлениеКлючейДоступа(Ложь, ПодсистемаСуществует);
		Если ДополнительныеПараметры.ЗаписыватьВЖурнал Тогда
			ЗаписатьДетальнуюИнформациюОХодеОбновления(ОписаниеОбработчика);
		КонецЕсли;
		
		ИмяОбработчика = Обработчик.Процедура + "(" + ?(ПараметрыОбработчика = Неопределено, "", "Параметры") + ")";
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'При вызове обработчика обновления:
					   |""%1""
					   |произошла ошибка:
					   |""%2"".'"),
			ИмяОбработчика,
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		
		ЗаписатьОшибку(ТекстОшибки);
		УстановитьСтатусОбработчика(Обработчик.Процедура, "Ошибка", ТекстОшибки);
		ВызватьИсключение;
	КонецПопытки;
	
	Если ДополнительныеПараметры.ЗаписыватьВЖурнал Тогда
		ЗаписатьДетальнуюИнформациюОХодеОбновления(ОписаниеОбработчика);
	КонецЕсли;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОценкаПроизводительности") Тогда
		МодульОценкаПроизводительности = ОбщегоНазначения.ОбщийМодуль("ОценкаПроизводительности");
		МодульОценкаПроизводительности.ЗакончитьЗамерВремениТехнологический("ВремяВыполненияОбработчикаОбновления." + ОписаниеОбработчика.Процедура, НачалоЗамера);
	КонецЕсли;
	
КонецПроцедуры

Процедура ВыполнитьОбработчикиПослеОбновленияИнформационнойБазы(Знач ИтерацииОбновления, Знач ЗаписыватьВЖурнал, ВыводитьОписаниеОбновлений, Знач ОперативноеОбновление)
	
	Для Каждого ИтерацияОбновления Из ИтерацииОбновления Цикл
		
		Если ЗаписыватьВЖурнал Тогда
			Обработчик = Новый Структура();
			Обработчик.Вставить("Версия", "*");
			Обработчик.Вставить("ВерсияРегистрации", "*");
			Обработчик.Вставить("РежимВыполнения", "Оперативно");
			Обработчик.Вставить("Процедура", ИтерацияОбновления.ИмяОсновногоСерверногоМодуля + ".ПослеОбновленияИнформационнойБазы");
			ОписаниеОбработчика =  ПодготовитьДетальнуюИнформациюОХодеОбновления(Обработчик, Неопределено, ИтерацияОбновления.Подсистема);
		КонецЕсли;
		
		Попытка
			
			ИтерацияОбновления.ОсновнойСерверныйМодуль.ПослеОбновленияИнформационнойБазы(
				ИтерацияОбновления.ПредыдущаяВерсия,
				ИтерацияОбновления.Версия,
				ИтерацияОбновления.ВыполненныеОбработчики,
				ВыводитьОписаниеОбновлений,
				НЕ ОперативноеОбновление);
				
		Исключение
			
			Если ЗаписыватьВЖурнал Тогда
				ЗаписатьДетальнуюИнформациюОХодеОбновления(ОписаниеОбработчика);
			КонецЕсли;
			
			ВызватьИсключение;
			
		КонецПопытки;
		
		Если ЗаписыватьВЖурнал Тогда
			ЗаписатьДетальнуюИнформациюОХодеОбновления(ОписаниеОбработчика);
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//  Обработчик - Неопределено
//             - СтрокаДереваЗначений
//             - Структура:
//    * РежимВыполнения - Строка
//    * ВерсияРегистрации - Строка
//    * Версия - Строка
//  Параметры - Неопределено  
//            - Структура:
//    * Очередь - Произвольный
//    * ПрогрессВыполнения - Структура:
//        ** ОбработаноОбъектов - Число
//        ** ВсегоОбъектов - Число
//    * ОбработкаЗавершена - Булево
//  ИдентификаторБиблиотеки - Произвольный
//  ОбработчикОтложенный - Булево
// Возвращаемое значение:
//  Структура:
//   * ЗначениеНаНачало - Число
//   * ОбластьДанныхИспользование - Булево
//   * ОбластьДанныхЗначение - Число
//   * РежимВыполнения - Строка
//   * Параметры - Неопределено
// 
Функция ПодготовитьДетальнуюИнформациюОХодеОбновления(Обработчик, Параметры, ИдентификаторБиблиотеки, ОбработчикОтложенный = Ложь)
	
	ОписаниеОбработчика = Новый Структура;
	ОписаниеОбработчика.Вставить("Библиотека", ИдентификаторБиблиотеки);
	Если ОбработчикОтложенный Тогда
		ОписаниеОбработчика.Вставить("Версия", Обработчик.Версия);
		ОписаниеОбработчика.Вставить("Процедура", Обработчик.ИмяОбработчика);
	Иначе
		ОписаниеОбработчика.Вставить("Версия", Обработчик.Версия);
		ОписаниеОбработчика.Вставить("Процедура", Обработчик.Процедура);
	КонецЕсли;
	ОписаниеОбработчика.Вставить("ВерсияРегистрации", Обработчик.ВерсияРегистрации);
	ОписаниеОбработчика.Вставить("Параметры", Параметры);
	
	Если ОбработчикОтложенный Тогда
		ОписаниеОбработчика.Вставить("РежимВыполнения", "Отложенно");
	ИначеЕсли ЗначениеЗаполнено(Обработчик.РежимВыполнения) Тогда
		ОписаниеОбработчика.Вставить("РежимВыполнения", Обработчик.РежимВыполнения);
	Иначе
		ОписаниеОбработчика.Вставить("РежимВыполнения", "Монопольно");
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено()
	   И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		
		МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
		
		ОписаниеОбработчика.Вставить("ОбластьДанныхЗначение",
			МодульРаботаВМоделиСервиса.ЗначениеРазделителяСеанса());
		ОписаниеОбработчика.Вставить("ОбластьДанныхИспользование", Истина);
		
	Иначе
		
		ОписаниеОбработчика.Вставить("ОбластьДанныхЗначение", -1);
		ОписаниеОбработчика.Вставить("ОбластьДанныхИспользование", Ложь);
		
	КонецЕсли;
	
	ОписаниеОбработчика.Вставить("ЗначениеНаНачало", ТекущаяУниверсальнаяДатаВМиллисекундах());
	
	Возврат ОписаниеОбработчика;
	
КонецФункции

Процедура ЗаписатьДетальнуюИнформациюОХодеОбновления(ОписаниеОбработчика)
	
	Длительность = ТекущаяУниверсальнаяДатаВМиллисекундах() - ОписаниеОбработчика.ЗначениеНаНачало;
	
	ОписаниеОбработчика.Вставить("Выполнен", Ложь);
	ОписаниеОбработчика.Вставить("Длительность", Длительность / 1000); // В секундах
	
	КопияОписания = ОбщегоНазначения.СкопироватьРекурсивно(ОписаниеОбработчика);
	Если КопияОписания.Свойство("Параметры") Тогда
		КопияОписания.Удалить("Параметры");
	КонецЕсли;
	
	ЗаписьЖурналаРегистрации(
		СобытиеЖурналаРегистрацииПротокол(),
		УровеньЖурналаРегистрации.Информация,
		,
		,
		ОбщегоНазначения.ЗначениеВСтрокуXML(КопияОписания));
		
КонецПроцедуры

Процедура ПроверитьВложеннуюТранзакциюПриВыполненииОтложенногоОбработчика(КонтекстОбработчика, Результат)
	
	Попытка
		ПроверитьВложеннуюТранзакцию(КонтекстОбработчика.ТранзакцияАктивнаНаНачалоВыполнения,
			КонтекстОбработчика.ИмяОбработчика);
	Исключение
		Результат.ИнформацияОбОшибке = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		Результат.ЕстьНеЗакрытыеТранзакции = Истина;
		
		Пока ТранзакцияАктивна() Цикл
			ОтменитьТранзакцию(); // АПК:325 Отмена незакрытых транзакций.
		КонецЦикла;
	КонецПопытки;
	
КонецПроцедуры

Процедура ПроверитьВложеннуюТранзакцию(ТранзакцияАктивнаНаНачалоВыполнения, НазваниеОбработчика)
	
	ИмяСобытия = СобытиеЖурналаРегистрации() + "." + НСтр("ru = 'Выполнение обработчиков'", ОбщегоНазначения.КодОсновногоЯзыка());
	Если ТранзакцияАктивнаНаНачалоВыполнения Тогда
		
		Если ТранзакцияАктивна() Тогда
			// Проверка поглощенных исключений в обработчиках.
			Попытка
				Константы.ИспользоватьРазделениеПоОбластямДанных.Получить();
			Исключение
				ШаблонКомментария = НСтр("ru = 'Ошибка выполнения обработчика обновления %1:
				|Обработчиком обновления было поглощено исключение при активной внешней транзакции.
				|При активных транзакциях, открытых выше по стеку, исключение также необходимо пробрасывать выше по стеку.'");
				Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонКомментария, НазваниеОбработчика);
				
				ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка,,, Комментарий);
				ВызватьИсключение(Комментарий);
			КонецПопытки;
		Иначе
			ШаблонКомментария = НСтр("ru = 'Ошибка выполнения обработчика обновления %1:
			|Обработчиком обновления была закрыта лишняя транзакция, открытая ранее (выше по стеку).'");
			Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонКомментария, НазваниеОбработчика);
			
			ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка,,, Комментарий);
			ВызватьИсключение(Комментарий);
		КонецЕсли;
	Иначе
		Если ТранзакцияАктивна() Тогда
			ШаблонКомментария = НСтр("ru = 'Ошибка выполнения обработчика обновления %1:
			|Открытая внутри обработчика обновления транзакция осталась активной (не была закрыта или отменена).'");
			Комментарий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонКомментария, НазваниеОбработчика);
			
			ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка,,, Комментарий);
			ВызватьИсключение(Комментарий);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

Процедура ПроверитьСвойстваОбработчиков(ИтерацияОбновления)
	
	Для каждого Обработчик Из ИтерацияОбновления.Обработчики Цикл
		ОписаниеОшибки = "";
		
		// Обратная совместимость
		Если Обработчик.РежимВыполнения = "Исключительный" Тогда
			Обработчик.РежимВыполнения = "Монопольно";
		КонецЕсли;
		
		Если ПустаяСтрока(Обработчик.Версия) Тогда
			
			Если Обработчик.НачальноеЗаполнение <> Истина Тогда
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'У обработчика не заполнено свойство %1 или свойство %2.'"),
					"Версия", "НачальноеЗаполнение");
			КонецЕсли;
			
		ИначеЕсли Обработчик.Версия <> "*"
			И Не СтрНачинаетсяС(Обработчик.Версия, "ОтладкаОбработчика") Тогда
			
			Попытка
				НулеваяВерсия = ОбщегоНазначенияКлиентСервер.СравнитьВерсии(Обработчик.Версия, "0.0.0.0") = 0;
			Исключение
				НулеваяВерсия = Ложь;
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'У обработчика неправильно заполнено свойство %1: ""%2"".
					           |Правильный формат, например: ""2.1.3.70"".'"),
					"Версия", Обработчик.Версия);
			КонецПопытки;
			
			Если НулеваяВерсия Тогда
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'У обработчика неправильно заполнено свойство %1: ""%2"".
					           |Версия не может быть нулевой.'"),
					"Версия", Обработчик.Версия);
			КонецЕсли;
			
			Если НЕ ЗначениеЗаполнено(ОписаниеОшибки)
			   И Обработчик.ВыполнятьВГруппеОбязательных <> Истина
			   И Обработчик.Приоритет <> 0 Тогда
				
				ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'У обработчика неправильно заполнено свойство %1 или
				               |свойство %2.'"),
					"Приоритет", "ВыполнятьВГруппеОбязательных");
			КонецЕсли;
		КонецЕсли;
		
		Если Обработчик.РежимВыполнения <> ""
			И Обработчик.РежимВыполнения <> "Монопольно"
			И Обработчик.РежимВыполнения <> "Оперативно"
			И Обработчик.РежимВыполнения <> "Отложенно" Тогда
			ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'У обработчика ""%1"" неправильно заполнено свойство %2.
				           |Допустимое значение: ""%3"", ""%4"", ""%5"".'"),
				Обработчик.Процедура, "РежимВыполнения", "Монопольно", "Отложенно", "Оперативно");
		КонецЕсли;
		
		Если НЕ ЗначениеЗаполнено(ОписаниеОшибки)
		   И Обработчик.Опциональный = Истина
		   И Обработчик.НачальноеЗаполнение = Истина Тогда
			
			ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'У обработчика неправильно заполнено свойство %1 или
			               |свойство %2.'"),
				"Опциональный", "НачальноеЗаполнение");
		КонецЕсли;
			
		Если Не ЗначениеЗаполнено(ОписаниеОшибки) Тогда
			Продолжить;
		КонецЕсли;
		
		Если ИтерацияОбновления.ЭтоОсновнаяКонфигурация Тогда
			ЗаголовокОшибки = НСтр("ru = 'Ошибка в свойстве обработчика обновления конфигурации'");
		Иначе
			ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка в свойстве обработчика обновления библиотеки %1 версии %2'"),
				ИтерацияОбновления.Подсистема,
				ИтерацияОбновления.Версия);
		КонецЕсли;
		
		ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ЗаголовокОшибки + Символы.ПС
			+ НСтр("ru = '(%1).'") + Символы.ПС
			+ Символы.ПС
			+ ОписаниеОшибки,
			Обработчик.Процедура);
		
		ЗаписатьОшибку(ОписаниеОшибки);
		ВызватьИсключение ОписаниеОшибки;

	КонецЦикла;
	
КонецПроцедуры

Функция КоличествоОбработчиковНаТекущуюВерсию(ИтерацииОбновления, РежимВыполненияОтложенногоОбновления)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.УстановитьПараметр("РежимВыполненияОтложенногоОбработчика", Перечисления.РежимыВыполненияОтложенныхОбработчиков.Параллельно);
	
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения <> &РежимВыполнения";
	КоличествоРазделенныхОбработчиков = Запрос.Выполнить().Выгрузить().Количество();
	
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновленияОбщихДанных.ИмяОбработчика КАК ИмяОбработчика
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновленияОбщихДанных КАК ОбработчикиОбновленияОбщихДанных";
	КоличествоНеразделенныхОбработчиков = Запрос.Выполнить().Выгрузить().Количество();
	
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения";
	КоличествоОтложенныхОбработчиков = Запрос.Выполнить().Выгрузить().Количество();
	
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполненияОтложенногоОбработчика = &РежимВыполненияОтложенногоОбработчика";
	КоличествоПроцедурРегистрации = Запрос.Выполнить().Выгрузить().Количество();
	
	КоличествоОбработчиков = КоличествоРазделенныхОбработчиков + КоличествоНеразделенныхОбработчиков + КоличествоПроцедурРегистрации;
	Если РежимВыполненияОтложенногоОбновления = "Монопольно" Тогда
		КоличествоОбработчиков = КоличествоОбработчиков + КоличествоОтложенныхОбработчиков;
	КонецЕсли;
	
	Возврат Новый Структура("ВсегоОбработчиков, ВыполненоОбработчиков", КоличествоОбработчиков, 0);
	
КонецФункции

Функция ИмяОбъектаМетаданныхПоИмениМенеджера(ИмяМенеджера)
	
	Позиция = СтрНайти(ИмяМенеджера, ".");
	Если Позиция = 0 Тогда
		Возврат "ОбщийМодуль." + ИмяМенеджера;
	КонецЕсли;
	ТипМенеджера = Лев(ИмяМенеджера, Позиция - 1);
	
	ИменаТипов = Новый Соответствие;
	ИменаТипов.Вставить("Справочники", "Справочник");
	ИменаТипов.Вставить("Документы", "Документ");
	ИменаТипов.Вставить("Обработки", "Обработка");
	ИменаТипов.Вставить("ПланыВидовХарактеристик", "ПланВидовХарактеристик");
	ИменаТипов.Вставить("РегистрыБухгалтерии", "РегистрБухгалтерии");
	ИменаТипов.Вставить("РегистрыНакопления", "РегистрНакопления");
	ИменаТипов.Вставить("РегистрыРасчета", "РегистрРасчета");
	ИменаТипов.Вставить("РегистрыСведений", "РегистрСведений");
	ИменаТипов.Вставить("БизнесПроцессы", "БизнесПроцесс");
	ИменаТипов.Вставить("ЖурналыДокументов", "ЖурналДокументов");
	ИменаТипов.Вставить("Задачи", "Задача");
	ИменаТипов.Вставить("Отчеты", "Отчет");
	ИменаТипов.Вставить("Константы", "Константа");
	ИменаТипов.Вставить("Перечисления", "Перечисление");
	ИменаТипов.Вставить("ПланыВидовРасчета", "ПланВидовРасчета");
	ИменаТипов.Вставить("ПланыОбмена", "ПланОбмена");
	ИменаТипов.Вставить("ПланыСчетов", "ПланСчетов");
	
	ИмяТипа = ИменаТипов[ТипМенеджера];
	Если ИмяТипа = Неопределено Тогда
		Возврат ИмяМенеджера;
	КонецЕсли;
	
	Возврат ИмяТипа + Сред(ИмяМенеджера, Позиция);
КонецФункции

Процедура ОтметитьОбработчикиНовыхПодсистем(ВсеОбработчики)
	
	// Список объектов новых подсистем.
	ОбъектыНовыхПодсистем = Новый Массив;
	Для Каждого ИмяПодсистемы Из СведенияОбОбновленииИнформационнойБазы().НовыеПодсистемы Цикл
		Подсистема = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ИмяПодсистемы);
		Если Подсистема = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Для Каждого ОбъектМетаданных Из Подсистема.Состав Цикл
			ОбъектыНовыхПодсистем.Добавить(ОбъектМетаданных.ПолноеИмя());
		КонецЦикла;
	КонецЦикла;
	
	// Определение обработчиков новых подсистем.
	ВсеОбработчики.Колонки.Добавить("ЭтоНоваяПодсистема", Новый ОписаниеТипов("Булево"));
	Для Каждого ОписаниеОбработчика Из ВсеОбработчики Цикл
		Позиция = СтрНайти(ОписаниеОбработчика.Процедура, ".", НаправлениеПоиска.СКонца);
		ИмяМенеджера = Лев(ОписаниеОбработчика.Процедура, Позиция - 1);
		Если ОбъектыНовыхПодсистем.Найти(ИмяОбъектаМетаданныхПоИмениМенеджера(ИмяМенеджера)) <> Неопределено Тогда
			ОписаниеОбработчика.ЭтоНоваяПодсистема = Истина;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхПодчиненному.
Процедура ПриОтправкеВерсийПодсистем(ЭлементДанных, ОтправкаЭлемента, Знач СозданиеНачальногоОбраза = Ложь)
	
	СтандартнаяОбработка = Истина;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ОбновлениеВерсииИБВМоделиСервиса") Тогда
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса");
		МодульОбновлениеИнформационнойБазыСлужебныйВМоделиСервиса.ПриОтправкеВерсийПодсистем(ЭлементДанных, ОтправкаЭлемента, 
			СозданиеНачальногоОбраза, СтандартнаяОбработка);
	КонецЕсли;
	
	Если Не СтандартнаяОбработка Тогда
		Возврат;
	КонецЕсли;
	
	Если ОтправкаЭлемента = ОтправкаЭлементаДанных.Удалить
		ИЛИ ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать Тогда
		
		// Стандартную обработку не переопределяем.
		
	ИначеЕсли ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ВерсииПодсистем") Тогда
		
		Если Не СозданиеНачальногоОбраза Тогда
			
			// Выгрузку регистра выполняем только при создании начального образа.
			ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать;
			
		КонецЕсли;
		
	КонецЕсли;
	
КонецПроцедуры

Функция ОтметкаНачалаОбновления()
	
	ОписаниеСеанса = Новый Структура;
	ОписаниеСеанса.Вставить("ИмяКомпьютера");
	ОписаниеСеанса.Вставить("ИмяПриложения");
	ОписаниеСеанса.Вставить("НачалоСеанса");
	ОписаниеСеанса.Вставить("НомерСеанса");
	ОписаниеСеанса.Вставить("НомерСоединения");
	ОписаниеСеанса.Вставить("Пользователь");
	ЗаполнитьЗначенияСвойств(ОписаниеСеанса, ПолучитьТекущийСеансИнформационнойБазы());
	Пользователь  = ОписаниеСеанса.Пользователь; // ПользовательИнформационнойБазы
	ОписаниеСеанса.Пользователь = Пользователь.Имя;
	ИмяПараметра = "СтандартныеПодсистемы.ОбновлениеВерсииИБ.СеансОбновленияИнформационнойБазы";
	
	МожноВыполнятьОбновление = Истина;
	
	Блокировка = Новый БлокировкаДанных;
	Если ОбщегоНазначения.РазделениеВключено()
		И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		ЭлементБлокировки = Блокировка.Добавить("Константа.СведенияОбОбновленииИБ");
	Иначе
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыРаботыПрограммы");
		ЭлементБлокировки.УстановитьЗначение("ИмяПараметра", ИмяПараметра);
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		СохраненныеПараметры = СведенияОСеансеОбновления(ИмяПараметра);
		
		Если СохраненныеПараметры = Неопределено Тогда
			СеансыСовпадают = Ложь;
		Иначе
			СеансыСовпадают = ДанныеСовпадают(ОписаниеСеанса, СохраненныеПараметры);
		КонецЕсли;
		
		Если Не СеансыСовпадают Тогда
			СеансОбновленияАктивен = СеансАктивен(СохраненныеПараметры);
			Если СеансОбновленияАктивен Тогда
				СеансОбновления = СохраненныеПараметры;
				МожноВыполнятьОбновление = Ложь;
			Иначе
				ЗаписатьСведенияОСеансеОбновления(ИмяПараметра, ОписаниеСеанса);
				СеансОбновления = ОписаниеСеанса;
			КонецЕсли;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Результат = Новый Структура;
	Результат.Вставить("МожноВыполнятьОбновление", МожноВыполнятьОбновление);
	Результат.Вставить("СеансОбновления", СеансОбновления);
	
	Возврат Результат;
	
КонецФункции

Функция СведенияОСеансеОбновления(ИмяПараметра)
	Если ОбщегоНазначения.РазделениеВключено()
		И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Сведения = СведенияОбОбновленииИнформационнойБазы();
		СеансОбновления = Сведения.СеансОбновления;
	Иначе
		СеансОбновления = СтандартныеПодсистемыСервер.ПараметрРаботыПрограммы(ИмяПараметра);
	КонецЕсли;
	
	Возврат СеансОбновления;
КонецФункции

Процедура ЗаписатьСведенияОСеансеОбновления(ИмяПараметра, ОписаниеСеанса)
	Если ОбщегоНазначения.РазделениеВключено()
		И ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Сведения = СведенияОбОбновленииИнформационнойБазы();
		Сведения.СеансОбновления = ОписаниеСеанса;
		ЗаписатьСведенияОбОбновленииИнформационнойБазы(Сведения);
	Иначе
		СтандартныеПодсистемыСервер.УстановитьПараметрРаботыПрограммы(ИмяПараметра, ОписаниеСеанса);
	КонецЕсли;
КонецПроцедуры

Функция СеансАктивен(ОписаниеСеанса)
	Если ОписаниеСеанса = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	СеансыИнформационнойБазы = ПолучитьСеансыИнформационнойБазы();
	
	Для Каждого Сеанс Из СеансыИнформационнойБазы Цикл
		Совпадают = ДанныеСовпадают(ОписаниеСеанса, Сеанс);
		Если Совпадают Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Совпадают;
КонецФункции

Функция ДанныеСовпадают(Данные1, Данные2)
	
	Совпадают = Истина;
	Для Каждого КлючИЗначение Из Данные1 Цикл
		Если КлючИЗначение.Ключ = "Пользователь"
		 Или КлючИЗначение.Ключ = "НомерСоединения" Тогда
			Продолжить;
		КонецЕсли;
		
		Если Данные2[КлючИЗначение.Ключ] <> КлючИЗначение.Значение Тогда
			Совпадают = Ложь;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Совпадают;
	
КонецФункции

Функция НоваяТаблицаИнформацииОбОбработчиках() Экспорт

	ИнфоОбработчиков = Новый ТаблицаЗначений;
	ИнфоОбработчиков.Колонки.Добавить("ИмяОбработчика", Новый ОписаниеТипов("Строка"));
	ИнфоОбработчиков.Колонки.Добавить("РежимВыполнения", Новый ОписаниеТипов("Строка"));
	ИнфоОбработчиков.Колонки.Добавить("ИмяБиблиотеки", Новый ОписаниеТипов("Строка"));
	ИнфоОбработчиков.Колонки.Добавить("Версия", Новый ОписаниеТипов("Строка"));
	ИнфоОбработчиков.Колонки.Добавить("Статус", Новый ОписаниеТипов("Строка"));
	ИнфоОбработчиков.Колонки.Добавить("ДлительностьОбработки", Новый ОписаниеТипов("Число"));
	ИнфоОбработчиков.Колонки.Добавить("ИнформацияОбОшибке", Новый ОписаниеТипов("Строка"));
	ИнфоОбработчиков.Колонки.Добавить("ОбластьДанных", Новый ОписаниеТипов("Число"));

	Возврат ИнфоОбработчиков;

КонецФункции

Функция НовыйПрогрессОбновленияОбластейДанных() Экспорт

	Прогресс = Новый Структура;
	Прогресс.Вставить("Обновлено", 0);
	Прогресс.Вставить("Выполняется", 0);
	Прогресс.Вставить("Ожидают", 0);
	Прогресс.Вставить("Проблемы", 0);

	Прогресс.Вставить("ОбластиОбновлено", Новый Массив);
	Прогресс.Вставить("ОбластиВыполняется", Новый Массив);
	Прогресс.Вставить("ОбластиОжидает", Новый Массив);
	Прогресс.Вставить("ОбластиСПроблемами", Новый Массив);

	Возврат Прогресс;

КонецФункции

Функция ИменаПоЗначениямПеречисления(МетаданныеПеречисления) Экспорт

	МенеджерПеречисления = Перечисления[МетаданныеПеречисления.Имя];
	Результат = Новый Соответствие;
	Для Каждого ЭлементКоллекции Из МетаданныеПеречисления.ЗначенияПеречисления Цикл // ОбъектМетаданных
		Результат.Вставить(МенеджерПеречисления[ЭлементКоллекции.Имя], ЭлементКоллекции.Имя);
	КонецЦикла;

	Возврат Результат;

КонецФункции

Функция ЗначениеПеречисленияПоИмени(ИмяЗначения, МетаданныеПеречисления) Экспорт

	МенеджерПеречисления = Перечисления[МетаданныеПеречисления.Имя];
	Если Не ЗначениеЗаполнено(ИмяЗначения) Тогда
		Возврат МенеджерПеречисления.ПустаяСсылка();
	КонецЕсли;

	Результат = Неопределено;
	ДоступныеЗначения = Новый Массив;
	
	ИмяЗначенияДляСравнения = ВРег(ИмяЗначения);
	Для Каждого ЭлементКоллекции Из МетаданныеПеречисления.ЗначенияПеречисления Цикл // ОбъектМетаданных
		Если ВРег(ЭлементКоллекции.Имя) = ИмяЗначенияДляСравнения Тогда
			Результат = МенеджерПеречисления[ЭлементКоллекции.Имя];
			Прервать;
		КонецЕсли;
		ДоступныеЗначения.Добавить("""" + ЭлементКоллекции.Имя + """");
	КонецЦикла;

	Если Результат = Неопределено Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректное имя ""%1"" значения перечисления ""%2"".
				 |Доступные значения: %3'"), ИмяЗначения, МетаданныеПеречисления, СтрСоединить(ДоступныеЗначения, ", "));
	КонецЕсли;

	Возврат Результат;

КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Протоколирование хода обновления.

Процедура ЗаписатьИнформацию(Знач Текст) Экспорт
	
	ЖурналРегистрации.ДобавитьСообщениеДляЖурналаРегистрации(СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Информация,,, Текст);
	
КонецПроцедуры

Процедура ЗаписатьОшибку(Знач Текст) Экспорт
	
	ЖурналРегистрации.ДобавитьСообщениеДляЖурналаРегистрации(СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Ошибка,,, Текст);
	
КонецПроцедуры

Процедура ЗаписатьПредупреждение(Знач Текст) Экспорт
	
	ЖурналРегистрации.ДобавитьСообщениеДляЖурналаРегистрации(СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Предупреждение,,, Текст);
	
КонецПроцедуры

Процедура ЗаписатьИнформациюОХодеОбновления(Обработчик, ХодВыполненияОбработчиков, ВФоне)
	
	Если ХодВыполненияОбработчиков = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ХодВыполненияОбработчиков.ВыполненоОбработчиков = ХодВыполненияОбработчиков.ВыполненоОбработчиков + 1;
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		Сообщение = НСтр("ru = 'Выполняется обработчик обновления %1 (%2 из %3).'");
		Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			Сообщение, Обработчик.Процедура,
			ХодВыполненияОбработчиков.ВыполненоОбработчиков, ХодВыполненияОбработчиков.ВсегоОбработчиков);
		ЗаписатьИнформацию(Сообщение);
	КонецЕсли;
	
	Если ВФоне Тогда
		Прогресс = 10 + ХодВыполненияОбработчиков.ВыполненоОбработчиков / ХодВыполненияОбработчиков.ВсегоОбработчиков * 90;
		ДлительныеОперации.СообщитьПрогресс(Прогресс);
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Описание обновлений

// Вывести описания изменений в указанной версии.
//
// Параметры:
//  НомерВерсии  - Строка - номер версии, для которого выводится описание из макета
//                          табличного документа МакетОписаниеОбновлений в табличный документ.
//                          ДокументОписаниеОбновлений.
//
Процедура ВывестиОписаниеИзменений(Знач НомерВерсии, ДокументОписаниеОбновлений, МакетОписаниеОбновлений)
	
	Номер = СтрЗаменить(НомерВерсии, ".", "_");
	
	Если МакетОписаниеОбновлений.Области.Найти("Шапка" + Номер) = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ДокументОписаниеОбновлений.Вывести(МакетОписаниеОбновлений.ПолучитьОбласть("Шапка" + Номер));
	ДокументОписаниеОбновлений.НачатьГруппуСтрок("Версия" + Номер);
	ДокументОписаниеОбновлений.Вывести(МакетОписаниеОбновлений.ПолучитьОбласть("Версия" + Номер));
	ДокументОписаниеОбновлений.ЗакончитьГруппуСтрок();
	ДокументОписаниеОбновлений.Вывести(МакетОписаниеОбновлений.ПолучитьОбласть("Отступ"));
	
КонецПроцедуры

Функция ПоследняяВерсияОтображенияИзмененийСистемы(Знач ИмяПользователя = Неопределено) Экспорт
	
	Если ИмяПользователя = Неопределено Тогда
		ИмяПользователя = ИмяПользователя();
	КонецЕсли;
	
	ПоследняяВерсия = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить("ОбновлениеИБ",
		"ПоследняяВерсияОтображенияИзмененийСистемы", , , ИмяПользователя);
	
	Возврат ПоследняяВерсия;
	
КонецФункции

Процедура ОпределитьВыводОписанияОбновлений(ВыводитьОписаниеОбновлений)
	
	Если ВыводитьОписаниеОбновлений И Не ОбщегоНазначения.РазделениеВключено() Тогда
		ОбщегоНазначения.ХранилищеОбщихНастроекСохранить("ОбновлениеИБ", "ВывестиОписаниеИзмененийДляАдминистратора", Истина, , ИмяПользователя());
	КонецЕсли;
	
	Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		СведенияОбОбновленииИБ = СведенияОбОбновленииИнформационнойБазы();
		СведенияОбОбновленииИБ.ВыводитьОписаниеОбновлений = ВыводитьОписаниеОбновлений;
		
		ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновленииИБ);
	КонецЕсли;
	
КонецПроцедуры

// Возвращает список разделов описания изменений системы.
//
// Возвращаемое значение:
//  СписокЗначений:
//    * Значение - Число - вес версии.
//    * Представление - Строка - версии.
//
Функция РазделыОписанияИзменений() Экспорт
	
	Разделы = Новый СписокЗначений;
	ВесВерсииМетаданных = ВесВерсии(Метаданные.Версия);
	
	МакетОписаниеОбновлений = Метаданные.ОбщиеМакеты.Найти("ОписаниеИзмененийСистемы");
	Если МакетОписаниеОбновлений <> Неопределено Тогда
		ПредикатВерсии = "Версия";
		ПредикатШапки = "Шапка";
		Макет = ПолучитьОбщийМакет(МакетОписаниеОбновлений);
		
		Для каждого Область Из Макет.Области Цикл
			Если СтрНайти(Область.Имя, ПредикатВерсии) = 0 Тогда
				Продолжить;
			КонецЕсли;
			
			ВерсияВФорматеОписания = Сред(Область.Имя, СтрДлина(ПредикатВерсии) + 1);
			
			Если Макет.Области.Найти(ПредикатШапки + ВерсияВФорматеОписания) = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			РазрядыВерсииСтроками = СтрРазделить(ВерсияВФорматеОписания, "_");
			Если РазрядыВерсииСтроками.Количество() <> 4 Тогда
				Продолжить;
			КонецЕсли;
			
			ВесВерсии = ВесВерсииИзМассиваСтрок(РазрядыВерсииСтроками);
			
			Версия = ""
				+ Число(РазрядыВерсииСтроками[0]) + "."
				+ Число(РазрядыВерсииСтроками[1]) + "."
				+ Число(РазрядыВерсииСтроками[2]) + "."
				+ Число(РазрядыВерсииСтроками[3]);
			
			Если ВесВерсии > ВесВерсииМетаданных Тогда
				ТекстИсключения = НСтр("ru = 'В общем макете %1 для одного из разделов изменений
					|установлена версия выше, чем в метаданных. (%2, должна быть %3)'");
				ТекстИсключения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения,
					"ОписаниеИзмененийСистемы", Версия, Метаданные.Версия);
				ВызватьИсключение ТекстИсключения;
			КонецЕсли;
			
			Разделы.Добавить(ВесВерсии, Версия);
		КонецЦикла;
		
		Разделы.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
	КонецЕсли;
	
	
	Возврат Разделы;
	
КонецФункции

Функция ВесВерсииИзМассиваСтрок(РазрядыВерсииСтроками)
	
	Возврат 0
		+ Число(РазрядыВерсииСтроками[0]) * 1000000000
		+ Число(РазрядыВерсииСтроками[1]) * 1000000
		+ Число(РазрядыВерсииСтроками[2]) * 1000
		+ Число(РазрядыВерсииСтроками[3]);
	
КонецФункции

Функция ПолучитьВерсииБольшеЗаданной(Разделы, Версия)
	
	Результат = Новый Массив;
	
	Если Разделы = Неопределено Тогда
		Разделы = РазделыОписанияИзменений();
	КонецЕсли;
	
	ВесВерсии = ВесВерсии(Версия);
	Для каждого ЭлементСписка Из Разделы Цикл
		Если ЭлементСписка.Значение <= ВесВерсии Тогда
			Продолжить;
		КонецЕсли;
		
		Результат.Добавить(ЭлементСписка.Представление);
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Процедура ОтключитьОтложенноеОбновление()
	
	ПриВключенииОтложенногоОбновления(Ложь);
	
КонецПроцедуры

Функция ВыполненыВсеОтложенныеОбработчики(СведенияОбОбновлении)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ОбработчикиОбновления.Статус КАК Статус
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|	И ОбработчикиОбновления.Статус <> &Статус";
	НевыполненныеОбработчики = Запрос.Выполнить().Выгрузить();
	
	Если НевыполненныеОбработчики.Количество() = 0 Тогда
		СведенияОбОбновлении.ВремяОкончаниеОтложенногоОбновления = ТекущаяДатаСеанса();
		СведенияОбОбновлении.ОтложенноеОбновлениеЗавершеноУспешно = Истина;
		СведенияОбОбновлении.ДлительностьЭтаповОбновления.Некритичные.Конец = ТекущаяДатаСеанса();
		ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
		Константы.ОтложенноеОбновлениеЗавершеноУспешно.Установить(Истина);
		Если Не ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ() Тогда
			Константы.ОтложенноеОбновлениеВГлавномУзлеЗавершеноУспешно.Установить(Истина);
		КонецЕсли;
		
		Возврат Истина;
	Иначе
		Возврат Ложь;
	КонецЕсли;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Вспомогательные процедуры и функции отложенного обновления.

Процедура ОбработчикПроверкиВеденияУчета(Проверка, ПараметрыПроверки) Экспорт
	
	// Обработка не требуется, регистрация проблемных объектов выполняется
	// из обработчиков обновления.
	Возврат;
	
КонецПроцедуры

Функция ВыводитьПроблемыСДанными() Экспорт
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтрольВеденияУчета") Тогда
		МодульКонтрольВеденияУчета = ОбщегоНазначения.ОбщийМодуль("КонтрольВеденияУчета");
		Возврат МодульКонтрольВеденияУчета.ПодсистемаДоступна();
	Иначе
		Возврат Ложь;
	КонецЕсли;
	
КонецФункции

Функция КоличествоПроблемСДанными() Экспорт
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтрольВеденияУчета")
		Или Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат 0;
	КонецЕсли;
	
	МодульКонтрольВеденияУчета = ОбщегоНазначения.ОбщийМодуль("КонтрольВеденияУчета");
	Проверка          = МодульКонтрольВеденияУчета.ПроверкаПоИдентификатору("ОбновлениеИнформационнойБазыПроблемаСДанными");
	КоличествоПроблем = МодульКонтрольВеденияУчета.КоличествоПроблемПоПравилуПроверки(Проверка);
	
	Возврат КоличествоПроблем;
	
КонецФункции

Процедура ОчиститьЗарегистрированныеПроблемыСДанными()
	
	Если Не ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.КонтрольВеденияУчета") Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	МодульКонтрольВеденияУчета = ОбщегоНазначения.ОбщийМодуль("КонтрольВеденияУчета");
	
	Проверка = МодульКонтрольВеденияУчета.ПроверкаПоИдентификатору("ОбновлениеИнформационнойБазыПроблемаСДанными");
	МодульКонтрольВеденияУчета.ОчиститьРезультатПоПроверке(Проверка);
	
КонецПроцедуры

// Только для внутреннего использования.
//
Функция ВыполнитьОтложенныйОбработчикОбновления(ПараметрыОбновления = Неопределено)
	
	КонтекстОбработчика = НовыйКонтекстОбработчика();
	ОбработчикОбновления = НайтиОбработчикОбновления(КонтекстОбработчика, ПараметрыОбновления);
	
	Если ОбработчикОбновления = "ПрерватьВыполнение" Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если ТипЗнч(ОбработчикОбновления) = Тип("СтрокаТаблицыЗначений") Тогда
		АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено);
		
		Попытка
			ОбработчикВыполнилсяБезОшибок = Ложь;
			Если ОбработчикОбновления.Многопоточный Тогда
				ДополнитьКонтекстМногопоточногоОбработчика(КонтекстОбработчика);
				ОбрабатываемыеДанные = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
				ОбрабатываемыеДанные.ПоследняяВыбраннаяЗапись = Неопределено;
				ОбрабатываемыеДанные.ПоискЗавершен = Ложь;
				ПараметрыВыборки = ОбрабатываемыеДанные.ПараметрыВыборки;
				ПроверитьПараметрыВыборки(ПараметрыВыборки);
				ПараметрыВыборки.МаксимумВыборки = ОбновлениеИнформационнойБазы.МаксимальноеКоличествоЗаписейВВыборке();
				ПараметрыПоиска = НовыеПараметрыПоискаПорции();
				ПараметрыПоиска.ПараметрыВыборки = ПараметрыВыборки;
				ПараметрыПоиска.ПоследняяВыбраннаяЗапись = ОбрабатываемыеДанные.ПоследняяВыбраннаяЗапись;
				ПараметрыОбхода = ПараметрыОбходаДанныхДляОбновления(ПараметрыПоиска);
				Очередь = КонтекстОбработчика.Параметры.Очередь;
				ИтераторОбхода = ТекущиеПараметрыОбхода(ПараметрыОбхода);
				ДополнительныеИсточникиДанных = ПараметрыВыборки.ДополнительныеИсточникиДанных;
				
				ИсходныеПараметрыОбработчика = КонтекстОбработчика.ПараметрыОбработчикаОбновления;
				
				Пока ИтераторОбхода <> Неопределено Цикл
					ОбработчикВыполнилсяБезОшибок = Ложь;
					СсылочныйОбъект = ИтераторОбхода.СсылочныйОбъект;
					ТабличныйОбъект = ИтераторОбхода.ТабличныйОбъект;
					НаборДанных     = НовыйНаборДанныхДляОбновления();
					ЗаписьДанных    = НаборДанных.Добавить();
					ПараметрыВыборки.ДополнительныеИсточникиДанных = ОбновлениеИнформационнойБазы.ИсточникиДанных(
						ДополнительныеИсточникиДанных,
						СсылочныйОбъект,
						ТабличныйОбъект);
					// Так как параметр сеанса не устанавливается на время выборки данных, то добавляем в параметры выборки
					// текущие параметры обработчика обновления.
					ПараметрыВыборки.Вставить("ПараметрыОбработчикаОбновления", КонтекстОбработчика.ПараметрыОбработчикаОбновления);
					
					ЗаписьДанных.Данные = ВыбратьДанныеПорции(ПараметрыВыборки, Очередь, СсылочныйОбъект, ТабличныйОбъект);
					
					КонтекстОбработчика.ПараметрыОбработчикаОбновления = ПараметрыВыборки.ПараметрыОбработчикаОбновления;
					
					ЗаписьДанных.СсылочныйОбъект = СсылочныйОбъект;
					ЗаписьДанных.ТабличныйОбъект = ТабличныйОбъект;
					ОбновляемыеДанные = НоваяПорцияДляОбновления();
					ОбновляемыеДанные.НаборДанных = НаборДанных;
					
					Если ЗаписьДанных.Данные.Количество() > 0 Тогда
						ОбновляемыеДанные.ПерваяЗапись = КлючЗаписиПервойСтрокиНабораДанных(НаборДанных);
						ОбновляемыеДанные.ПоследняяЗапись = КлючЗаписиПоследнейСтрокиНабораДанных(НаборДанных);
					КонецЕсли;
					
					КонтекстОбработчика.Параметры.ОбновляемыеДанные = ОбновляемыеДанные;
					Количество = ЗаписьДанных.Данные.Количество();
					Если КонтекстОбработчика.ВыполнитьОбработчик Тогда
						ВыполнитьОтложенныйОбработчик(КонтекстОбработчика, АдресРезультата);
					КонецЕсли;
					ОбработчикВыполнилсяБезОшибок = Истина;
					ЗавершитьВыполнениеОтложенногоОбработчика(КонтекстОбработчика, АдресРезультата); // @skip-check query-in-loop - выполнение отложенных обработчиков.
					
					// Если обработчик исчерпал лимит попыток запуска - пропускаем его.
					ОбработчикОбновления = ОбработчикОбновления(КонтекстОбработчика.ИмяОбработчика); // @skip-check query-in-loop - выполнение отложенных обработчиков.
					МаксимумПопыток = МаксимумПопытокОбновления(ОбработчикОбновления);
					Если ОбработчикОбновления.ЧислоПопыток >= МаксимумПопыток Тогда
						Прервать;
					КонецЕсли;
					
					// Сброс параметров обработчика перед выполнением следующей порции.
					КонтекстОбработчика.ПараметрыОбработчикаОбновления = ИсходныеПараметрыОбработчика;
					
					Если Количество > 0 Тогда
						ОбрабатываемыеДанные.ПоследняяВыбраннаяЗапись = КлючЗаписиПоследнейСтрокиНабораДанных(НаборДанных);
					Иначе
						ОбрабатываемыеДанные.ПоследняяВыбраннаяЗапись = Неопределено;
					КонецЕсли;
					
					СледующиеПараметрыОбхода(ПараметрыОбхода, Количество = ПараметрыВыборки.МаксимумВыборки);
					ИтераторОбхода = ТекущиеПараметрыОбхода(ПараметрыОбхода);
				КонецЦикла;
				
				ПараметрыВыборки.ДополнительныеИсточникиДанных = ДополнительныеИсточникиДанных;
			Иначе
				Если КонтекстОбработчика.ВыполнитьОбработчик Тогда
					ВыполнитьОтложенныйОбработчик(КонтекстОбработчика, АдресРезультата);
				КонецЕсли;
				ОбработчикВыполнилсяБезОшибок = Истина;
				ЗавершитьВыполнениеОтложенногоОбработчика(КонтекстОбработчика, АдресРезультата);
			КонецЕсли;
		Исключение
			ОбработатьИсключениеОбработчика(КонтекстОбработчика, ОбработчикОбновления, ИнформацияОбОшибке());
			// Если обработчик вызвал исключение, то вызываем процедуру завершения обработчика.
			Если Не ОбработчикВыполнилсяБезОшибок Тогда
				КонтекстОбработчика.Вставить("ОбработкаИсключенияОбработчика");
				ЗавершитьВыполнениеОтложенногоОбработчика(КонтекстОбработчика, АдресРезультата);
			КонецЕсли;
		КонецПопытки;
	ИначеЕсли ОбработчикОбновления = Ложь Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Завершить выполнение отложенного обработчика в основном потоке, после завершения работы фонового задания.
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//
Процедура ЗавершитьВыполнениеОтложенногоОбработчикаОбновления(КонтекстОбработчика)
	
	ПараллельныйРежим = КонтекстОбработчика.ПараллельныйРежим;
	ТекущаяИтерацияОбновления = КонтекстОбработчика.ТекущаяИтерацияОбновления;
	
	ОбработчикОбновления = ОбработчикОбновления(КонтекстОбработчика.ИмяОбработчика);
	УстанавливаемыеСвойства = Новый Структура;
	
	Если ОбработчикОбновления.Статус = Перечисления.СтатусыОбработчиковОбновления.Выполнен Тогда
		
		СведенияОБлокируемыхОбъектах = СведенияОБлокируемыхОбъектах();
		СведенияОбОбработчике = СведенияОБлокируемыхОбъектах.Обработчики[ОбработчикОбновления.ИмяОбработчика];
		Если СведенияОбОбработчике <> Неопределено Тогда
			СведенияОбОбработчике.Выполнен = Истина;
			ЗаписатьСведенияОБлокируемыхОбъектах(СведенияОБлокируемыхОбъектах);
		КонецЕсли;
		УстанавливаемыеСвойства.Вставить("ИнформацияОбОшибке", "");
		
	ИначеЕсли ОбработчикОбновления.Статус = Перечисления.СтатусыОбработчиковОбновления.Выполняется Тогда
		
		// Если у обработчика стоит высокий приоритет,
		// то он вызывается пять раз, после чего вызывается следующий обработчик.
		ЗапускиСПриоритетом = Неопределено;
		Если ОбработчикОбновления.Приоритет = "ЖелательноБыстрее" Тогда
			СтатистикаВыполнения = ОбработчикОбновления.СтатистикаВыполнения.Получить();
			ЗапускиСПриоритетом = СтатистикаВыполнения["ЗапускиСПриоритетом"];
			ЗапускиСПриоритетом = ?(ЗапускиСПриоритетом = Неопределено, 1, ?(ЗапускиСПриоритетом = 4, 0, ЗапускиСПриоритетом + 1));
			СтатистикаВыполнения.Вставить("ЗапускиСПриоритетом", ЗапускиСПриоритетом);
			УстанавливаемыеСвойства.Вставить("СтатистикаВыполнения", Новый ХранилищеЗначения(СтатистикаВыполнения));
		КонецЕсли;
		
		Если ЗапускиСПриоритетом = Неопределено Или ЗапускиСПриоритетом = 0 Тогда
			УстанавливаемыеСвойства.Вставить("ИтерацияЗапуска", ТекущаяИтерацияОбновления);
		КонецЕсли;
		
	Иначе
		УстанавливаемыеСвойства.Вставить("ИтерацияЗапуска", ТекущаяИтерацияОбновления);
	КонецЕсли;
	
	УстановитьСвойстваОбработчика(ОбработчикОбновления.ИмяОбработчика, УстанавливаемыеСвойства);
	
	// В параллельном режиме обновление нужно останавливать, если обработчик не удалось выполнить,
	// т.к. от данных, которые он обрабатывает, могут зависеть другие обработчики.
	Если ПараллельныйРежим
		И ОбработчикОбновления.Статус = Перечисления.СтатусыОбработчиковОбновления.Ошибка
		И ОбработчикОбновления.ЧислоПопыток >= МаксимумПопытокОбновления(ОбработчикОбновления)
		И ОтсутствуютОбработчикиДляВыполнения() Тогда
		СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
		СведенияОбОбновлении.ВремяОкончаниеОтложенногоОбновления = ТекущаяДатаСеанса();
		СведенияОбОбновлении.ОтложенноеОбновлениеЗавершеноУспешно = Ложь;
		ЗаписатьСведенияОбОбновленииИнформационнойБазы(СведенияОбОбновлении);
		Константы.ОтложенноеОбновлениеЗавершеноУспешно.Установить(Ложь);
		Если Не ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ() Тогда
			Константы.ОтложенноеОбновлениеВГлавномУзлеЗавершеноУспешно.Установить(Ложь);
		КонецЕсли;
		
		ШаблонОшибки = НСтр("ru = 'Не удалось выполнить обработчик обновления ""%1"". Подробнее в журнале регистрации.'");
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
			КонтекстОбработчика.ИмяОбработчика);
		КонтекстОбработчика.Вставить("ОшибкаПриЗавершенииОбработчика", ТекстОшибки);
	КонецЕсли;
	
КонецПроцедуры

Функция ОтсутствуютОбработчикиДляВыполнения()
	
	Статусы = Новый Массив;
	Статусы.Добавить(Перечисления.СтатусыОбработчиковОбновления.НеВыполнялся);
	Статусы.Добавить(Перечисления.СтатусыОбработчиковОбновления.Выполняется);
	
	// Быстрая проверка, что есть обработчики не в статусе ошибка.
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Статусы", Статусы);
	Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.Статус В(&Статусы)";
	
	Если Не Запрос.Выполнить().Пустой() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Ошибка);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ИмяОбработчика КАК ИмяОбработчика,
		|	ОбработчикиОбновления.Статус КАК Статус,
		|	ОбработчикиОбновления.ЧислоПопыток КАК ЧислоПопыток,
		|	ОбработчикиОбновления.Многопоточный,
		|	ОбработчикиОбновления.ОбрабатываемыеДанные
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|	И ОбработчикиОбновления.Статус = &Статус";
	Обработчики = Запрос.Выполнить().Выгрузить();
	Для Каждого Обработчик Из Обработчики Цикл
		Если Обработчик.ЧислоПопыток < МаксимумПопытокОбновления(Обработчик) Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Истина;
	
КонецФункции

Функция ПереданныеПараметрыОбработчикаОбновления(Параметры)
	ПереданныеПараметры = Новый Структура;
	Для Каждого Параметр Из Параметры Цикл
		Если Параметр.Ключ <> "ОбработкаЗавершена"
			И Параметр.Ключ <> "ПрогрессВыполнения"
			И Параметр.Ключ <> "Очередь"
			И Параметр.Ключ <> "ОбновляемыеДанные" Тогда
			ПереданныеПараметры.Вставить(Параметр.Ключ, Параметр.Значение);
		КонецЕсли;
	КонецЦикла;
	
	Возврат ПереданныеПараметры;
КонецФункции

Функция НовыеСведенияОбОбновлении(СтарыеСведения = Неопределено)
	
	СведенияОбОбновлении = Новый Структура;
	СведенияОбОбновлении.Вставить("ВремяНачалаОбновления");
	СведенияОбОбновлении.Вставить("ВремяОкончанияОбновления");
	СведенияОбОбновлении.Вставить("ПродолжительностьОбновления");
	СведенияОбОбновлении.Вставить("ВремяНачалаОтложенногоОбновления");
	СведенияОбОбновлении.Вставить("ВремяОкончаниеОтложенногоОбновления");
	СведенияОбОбновлении.Вставить("НомерСеанса", Новый СписокЗначений());
	СведенияОбОбновлении.Вставить("ОтложенноеОбновлениеЗавершеноУспешно");
	СведенияОбОбновлении.Вставить("ДеревоОбработчиков", Новый ДеревоЗначений());
	СведенияОбОбновлении.Вставить("ВерсияДереваОбработчиков", "");
	СведенияОбОбновлении.Вставить("ВыводитьОписаниеОбновлений", Ложь);
	СведенияОбОбновлении.Вставить("ЛегальнаяВерсия", "");
	СведенияОбОбновлении.Вставить("НовыеПодсистемы", Новый Массив);
	СведенияОбОбновлении.Вставить("ВсеНовыеПодсистемы", Новый Массив);
	СведенияОбОбновлении.Вставить("УправлениеОтложеннымОбновлением", Новый Структура);
	СведенияОбОбновлении.Вставить("ТекущаяИтерацияОбновления", 1);
	СведенияОбОбновлении.Вставить("СеансОбновления");
	СведенияОбОбновлении.Вставить("ВерсияУдалениеПатчей");
	СведенияОбОбновлении.Вставить("ЗависимостьГруппОбработчиков", Новый Соответствие);
	СведенияОбОбновлении.Вставить("ВерсииПодсистемНаНачалоОбновления", Новый Соответствие);
	СведенияОбОбновлении.Вставить("ДатаНачалаСеансаОбновления", Неопределено);
	СведенияОбОбновлении.Вставить("ДлительностьЭтаповОбновления", Новый Структура);
	СведенияОбОбновлении.Вставить("ЧитаемыеИИзменяемыеТаблицы", Новый Соответствие);
	
	СведенияОбОбновлении.ДлительностьЭтаповОбновления.Вставить("Критичные", Новый Структура("Начало, Конец"));
	СведенияОбОбновлении.ДлительностьЭтаповОбновления.Вставить("Обычные", Новый Структура("Начало, Конец"));
	СведенияОбОбновлении.ДлительностьЭтаповОбновления.Вставить("Некритичные", Новый Структура("Начало, Конец"));
	
	Если ТипЗнч(СтарыеСведения) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(СведенияОбОбновлении, СтарыеСведения);
	КонецЕсли;
	
	Возврат СведенияОбОбновлении;
	
КонецФункции

Функция РежимВыполненияОтложенногоОбновления(ПараметрыОбновления)
	
	ИнформационнаяБазаФайловая             = ОбщегоНазначения.ИнформационнаяБазаФайловая();
	РазделениеВключено                     = ОбщегоНазначения.РазделениеВключено();
	ДоступноИспользованиеРазделенныхДанных = ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных();
	ВыполнятьОтложенныеОбработчики         = ПараметрыОбновления.ВыполнятьОтложенныеОбработчики;
	ПараметрЗапускаКлиента                 = СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере().Получить("ПараметрЗапуска");
	
	Если Не РазделениеВключено Или ДоступноИспользованиеРазделенныхДанных Тогда
		Если ИнформационнаяБазаФайловая
			Или СтрНайти(НРег(ПараметрЗапускаКлиента), НРег("ВыполнитьОтложенноеОбновлениеСейчас")) > 0
			Или ВыполнятьОтложенныеОбработчики Тогда
			Возврат "Монопольно";
		Иначе
			Возврат "Отложенно";
		КонецЕсли;
	Иначе
		Возврат Неопределено;
	КонецЕсли;
	
КонецФункции

// Получает сведения об обновлении информационной базы из константы "СведенияОбОбновленииИБ".
//
Функция СведенияОБлокируемыхОбъектах() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если ОбщегоНазначения.РазделениеВключено()
	   И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		
		Возврат НовыеСведенияОБлокируемыхОбъектах();
	КонецЕсли;
	
	СведенияОБлокируемыхОбъектах = Константы.СведенияОБлокируемыхОбъектах.Получить().Получить();
	Если ТипЗнч(СведенияОБлокируемыхОбъектах) <> Тип("Структура") Тогда
		Возврат НовыеСведенияОБлокируемыхОбъектах();
	КонецЕсли;
	
	СведенияОБлокируемыхОбъектах = НовыеСведенияОБлокируемыхОбъектах(СведенияОБлокируемыхОбъектах);
	Возврат СведенияОБлокируемыхОбъектах;
	
КонецФункции

// Подготовка запуска обработчика обновления в основном потоке.
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  ОбработчикОбновления - СтрокаДереваЗначений - обработчик обновления в виде строки дерева обработчиков.
//  ПараметрыОбновления - см. ВыполнитьОбновлениеИнформационнойБазы
//  СведенияОбОбновлении - см. СведенияОбОбновленииИнформационнойБазы
//
Процедура ПередЗапускомПроцедурыОбработкиДанных(КонтекстОбработчика,
                                                ОбработчикОбновления,
                                                ПараметрыОбновления,
                                                СведенияОбОбновлении)
	
	КонтекстОбработчика.ЗаписыватьВЖурнал = Константы.ДетализироватьОбновлениеИБВЖурналеРегистрации.Получить();
	КонтекстОбработчика.ТранзакцияАктивнаНаНачалоВыполнения = ТранзакцияАктивна();
	
	Попытка
		ИмяОбработчика = ОбработчикОбновления.ИмяОбработчика;
		ВерсииПодсистемНаНачалоОбновления = СведенияОбОбновлении.ВерсииПодсистемНаНачалоОбновления;
		ВерсияПодсистемыНаНачалоОбновления = ВерсииПодсистемНаНачалоОбновления[ОбработчикОбновления.ИмяБиблиотеки];
		
		КонтекстОбработчика.ЗапускВыполненБезОшибок = Истина;
		СообщениеОЗапущенномОбработчике = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Выполняется процедура обновления ""%1"".'"), ИмяОбработчика);
		ЖурналРегистрации.ДобавитьСообщениеДляЖурналаРегистрации(СобытиеЖурналаРегистрации(),
				УровеньЖурналаРегистрации.Информация,,, СообщениеОЗапущенномОбработчике);
		
		// Прогресс выполнения процедуры обработки данных.
		ПрогрессВыполнения = Новый Структура;
		ПрогрессВыполнения.Вставить("ВсегоОбъектов", 0);
		ПрогрессВыполнения.Вставить("ОбработаноОбъектов", 0);
		СтатистикаВыполнения = ОбработчикОбновления.СтатистикаВыполнения.Получить();
		Если СтатистикаВыполнения["ПрогрессВыполнения"] <> Неопределено
			И ТипЗнч(СтатистикаВыполнения["ПрогрессВыполнения"]) = Тип("Структура") Тогда
			ЗаполнитьЗначенияСвойств(ПрогрессВыполнения, СтатистикаВыполнения["ПрогрессВыполнения"]);
		КонецЕсли;
		
		// Инициализация параметров обработчика.
		Параметры = СтатистикаВыполнения["ПараметрыОбработчика"];
		Если Параметры = Неопределено Тогда
			Параметры = Новый Структура;
		КонецЕсли;
		
		КонтекстОбработчика.Параметры = Параметры;
		
		Если ПараметрыОбновления.ПараллельныйРежим Тогда
			Параметры.Вставить("ОбработкаЗавершена", Неопределено);
		Иначе
			Параметры.Вставить("ОбработкаЗавершена", Истина);
		КонецЕсли;
		
		Параметры.Вставить("ИмяОбработчика", ИмяОбработчика);
		Параметры.Вставить("ПрогрессВыполнения", ПрогрессВыполнения);
		Параметры.Вставить("ВерсияПодсистемыНаНачалоОбновления", ВерсияПодсистемыНаНачалоОбновления);
		
		Параметры.Вставить("Очередь", ОбработчикОбновления.ОчередьОтложеннойОбработки);
		
		Если КонтекстОбработчика.ЗаписыватьВЖурнал Тогда
			КонтекстОбработчика.ДетальноеОписаниеОбработчика = ПодготовитьДетальнуюИнформациюОХодеОбновления(ОбработчикОбновления,
				Параметры,
				ОбработчикОбновления.ИмяБиблиотеки,
				Истина);
		КонецЕсли;
		
		КоличествоЗапусковПроцедурыОбновления = КоличествоЗапусковПроцедурыОбновления(СтатистикаВыполнения);
		ЧислоПотоковОбновления                = КоличествоПотоковОбновленияИнформационнойБазы();
		МаксимальноеКоличествоЗапусков        = ЧислоПотоковОбновления * 10000;
		
		Если КоличествоЗапусковПроцедурыОбновления > МаксимальноеКоличествоЗапусков Тогда // Защита от зацикливания.
			Если ПараметрыОбновления.ПараллельныйРежим
				И ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ()
				И ПараметрыОбновления.ЕстьОбработчикиГлавногоУзла Тогда
				ТекстОшибки = НСтр("ru = 'Превышено допустимое количество запусков процедуры обновления.
					|Убедитесь, что дополнительные процедуры обработки данных в главном узле
					|полностью завершились, выполните синхронизацию данных и повторно
					|запустите выполнение процедур обработки данных в данном узле.'");
			Иначе
				ТекстОшибки = НСтр("ru = 'Превышено допустимое количество запусков процедуры обновления.
					|Выполнение прервано для предотвращения зацикливания механизма обработки данных.'");
			КонецЕсли;
			
			МинимальнаяОчередь = МинимальнаяОчередьОтложеннойОбработки();
			
			Если Не ПараметрыОбновления.ПараллельныйРежим
				Или ОбработчикОбновления.ОчередьОтложеннойОбработки <= МинимальнаяОчередь Тогда
				ЧислоПопыток = МаксимумПопытокОбновления(ОбработчикОбновления);
				УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика, "ЧислоПопыток", ЧислоПопыток);
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
		КонецЕсли;
		
		// Запуск отложенного обработчика обновления.
		Если СтатистикаВыполнения["НачалоОбработкиДанных"] = Неопределено Тогда
			СтатистикаВыполнения.Вставить("НачалоОбработкиДанных", ТекущаяДатаСеанса());
		КонецЕсли;
		
		Свойства = Новый Структура;
		Свойства.Вставить("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполняется);
		Свойства.Вставить("ОбработкаПорцииЗавершена", Ложь);
		Свойства.Вставить("СтатистикаВыполнения", Новый ХранилищеЗначения(СтатистикаВыполнения));
		УстановитьСвойстваОбработчика(ОбработчикОбновления.ИмяОбработчика, Свойства);
		
		ОбрабатываемыеДанные = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
		Если ОбработчикОбновления.Многопоточный Тогда
			ПроверитьПараметрыВыборки(ОбрабатываемыеДанные.ПараметрыВыборки);
		КонецЕсли;
		
		КонтекстОбработчика.НачалоОбработкиДанных = ТекущаяУниверсальнаяДатаВМиллисекундах();
		Если ПараметрыОбновления.ПараллельныйРежим
			И ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ()
			И ОбработчикОбновления.ЗапускатьТолькоВГлавномУзле Тогда
			// В подчиненном узле РИБ только проверяем, что обрабатываемые обработчиком данные
			// пришли из главного узла и обновляем статус обработчика.
			КонтекстОбработчика.ПропуститьПроверкуОбработанныхДанных = Истина;
			ОписаниеОбрабатываемыхДанных = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
			ДанныеОбработчика = ОписаниеОбрабатываемыхДанных.ДанныеОбработчика;
			
			Если ДанныеОбработчика.Количество() = 0 Тогда
				Параметры.ОбработкаЗавершена = Истина;
			Иначе
				Для Каждого ОбрабатываемыйОбъект Из ДанныеОбработчика Цикл
					Параметры.ОбработкаЗавершена = ОбновлениеИнформационнойБазы.ОбработкаДанныхЗавершена(
						ОбработчикОбновления.ОчередьОтложеннойОбработки,
						ОбрабатываемыйОбъект.Ключ);
					Если Не Параметры.ОбработкаЗавершена Тогда
						Прервать;
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
		Иначе
			КонтекстОбработчика.ВыполнитьОбработчик = Истина;
			Возврат;
		КонецЕсли;
	Исключение
		ОбработатьИсключениеОбработчика(КонтекстОбработчика, ОбработчикОбновления, ИнформацияОбОшибке());
		КонтекстОбработчика.ЗапускВыполненБезОшибок = Ложь;
	КонецПопытки;
	
	ЗавершитьПроцедуруОбработкиДанных(КонтекстОбработчика, ОбработчикОбновления.ИмяОбработчика);
	
КонецПроцедуры

// Завершение запуска процедуры обработки данных в основном потоке.
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  ИмяОбработчика - Строка
//
Процедура ПослеЗапускаПроцедурыОбработкиДанных(КонтекстОбработчика, ИмяОбработчика)
	
	ОбработчикОбновления = ОбработчикОбновления(ИмяОбработчика);
	Параметры = КонтекстОбработчика.Параметры;
	ПараметрыОбновления = КонтекстОбработчика.ПараметрыОбновления;
	УстанавливаемыеСвойства = Новый Структура;
	
	Попытка
		ЗавершениеОбработкиДанных = ТекущаяУниверсальнаяДатаВМиллисекундах();
		ОписаниеПроцедурыЗаполнения = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
		
		Если КонтекстОбработчика.Свойство("ОбработкаИсключенияОбработчика") Тогда
			Параметры.ОбработкаЗавершена = Ложь;
		КонецЕсли;
		
		Если Параметры.ОбработкаЗавершена = Неопределено Тогда
			ТекстОшибки = НСтр("ru = 'Обработчик обновления не инициализировал параметр %1.
				|Выполнение прервано из-за явной ошибки в коде обработчика.'");
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстОшибки, "ОбработкаЗавершена");
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		СтатистикаВыполнения = ОбработчикОбновления.СтатистикаВыполнения.Получить();
		ДобавитьКоличествоЗапусков = Истина;
		Если Параметры.ОбработкаЗавершена Тогда
			СтатистикаВыполнения.Вставить("ЗавершениеОбработкиДанных", ТекущаяДатаСеанса());
			
			ЧистаяДлительностьВыполнения = ЧистаяДлительностьВыполненияОбработчика(ОбработчикОбновления, СтатистикаВыполнения);
			
			УстанавливаемыеСвойства.Вставить("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
			УстанавливаемыеСвойства.Вставить("Приоритет","ВПлановомПорядке");
			
			// Запись прогресса обновления.
			Если ПараметрыОбновления.Свойство("ВФоне")
				И ПараметрыОбновления.ВФоне Тогда
				ХодВыполненияОбработчиков = ПараметрыОбновления.ХодВыполненияОбработчиков;
				ХодВыполненияОбработчиков.ВыполненоОбработчиков = ХодВыполненияОбработчиков.ВыполненоОбработчиков + 1;
				Прогресс = 10 + ХодВыполненияОбработчиков.ВыполненоОбработчиков / ХодВыполненияОбработчиков.ВсегоОбработчиков * 90;
				ДлительныеОперации.СообщитьПрогресс(Прогресс);
			КонецЕсли;
		ИначеЕсли ПараметрыОбновления.ПараллельныйРежим И Не КонтекстОбработчика.ПропуститьПроверкуОбработанныхДанных Тогда
			ЕстьОбработанныеОбъекты = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ЕстьОбработанныеОбъекты;
			ОчередьОбработчика = ОбработчикОбновления.ОчередьОтложеннойОбработки;
			
			МинимальнаяОчередь = 0;
			Если ЕстьОбработанныеОбъекты Тогда
				// Дополнительная проверка, что не было отката транзакции.
				ИдентификаторТранзакции = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ИдентификаторТранзакции;
				МенеджерЗаписи = РегистрыСведений.ФиксацияОбработкиДанныхОбработчиками.СоздатьМенеджерЗаписи();
				МенеджерЗаписи.ИдентификаторТранзакции = ИдентификаторТранзакции;
				МенеджерЗаписи.Прочитать();
				ЕстьОбработанныеОбъекты = МенеджерЗаписи.Выбран();
				Если ЕстьОбработанныеОбъекты Тогда
					МенеджерЗаписи.Удалить();
				КонецЕсли;
			КонецЕсли;
			
			// Если в обработчике регистра данные разделяются по регистратору на критичные и некритичные, то получаем
			// таблицы, для которых актуальные данные уже обработаны.
			ОбработанныеТаблицыРегистраторов = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ОбработанныеТаблицыРегистраторов;
			Если ЗначениеЗаполнено(ОбработанныеТаблицыРегистраторов) Тогда
				Если ОписаниеПроцедурыЗаполнения.ОбработанныеТаблицыРегистраторов = Неопределено Тогда
					ОписаниеПроцедурыЗаполнения.ОбработанныеТаблицыРегистраторов = Новый Массив;
				КонецЕсли;
				ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ОписаниеПроцедурыЗаполнения.ОбработанныеТаблицыРегистраторов, ОбработанныеТаблицыРегистраторов, Истина);
				УстанавливаемыеСвойства.Вставить("ОбрабатываемыеДанные", Новый ХранилищеЗначения(ОписаниеПроцедурыЗаполнения));
				
				Если ОбщегоНазначения.КоллекцииИдентичны(ОписаниеПроцедурыЗаполнения.ОбработанныеТаблицыРегистраторов, ОписаниеПроцедурыЗаполнения.ЗарегистрированныеТаблицыРегистраторов) Тогда
					УстанавливаемыеСвойства.Вставить("ОбработаныАктуальныеДанные", Истина);
				КонецЕсли;
			КонецЕсли;
			
			// Проверка, что обработчик обработал актуальные данные.
			Если Не ЕстьОбработанныеОбъекты Тогда
				ОбработаныВсеАктуальныеДанные = ПараметрыСеанса.ПараметрыОбработчикаОбновления.ОбработаныАктуальныеДанные;
				Если ОбработаныВсеАктуальныеДанные = Истина Тогда
					УстанавливаемыеСвойства.Вставить("ОбработаныАктуальныеДанные", Истина);
				Иначе
					МинимальнаяОчередь = МинимальнаяОчередьОтложеннойОбработки();
				КонецЕсли;
			КонецЕсли;
			
			ДобавитьКоличествоЗапусков = (ЕстьОбработанныеОбъекты Или ОчередьОбработчика <= МинимальнаяОчередь);
			
			Если Не ЕстьОбработанныеОбъекты
				И ОчередьОбработчика <= МинимальнаяОчередь Тогда
				ЧислоПопыток = ОбработчикОбновления.ЧислоПопыток;
				МаксимумПопыток = МаксимумПопытокОбновления(ОбработчикОбновления) - 1;
				Если ЧислоПопыток >= МаксимумПопыток Тогда
					ТекстИсключения = НСтр("ru = 'Произошло зацикливание процедуры обработки данных. Выполнение прервано.'");
					ВызватьИсключение ТекстИсключения;
				Иначе
					ДобавляемоеЧислоПопыток = ДобавляемоеЧислоПопыток(ОбработчикОбновления, КонтекстОбработчика);
					УстанавливаемыеСвойства.Вставить("ЧислоПопыток", ЧислоПопыток + ДобавляемоеЧислоПопыток);
				КонецЕсли;
			Иначе
				УстанавливаемыеСвойства.Вставить("ЧислоПопыток", 0);
			КонецЕсли;
		КонецЕсли;
		
		// Сохранение данных по процедуре обработки данных.
		Если ОбработчикОбновления.Многопоточный Тогда
			ПрогрессВыполнения = СтатистикаВыполнения["ПрогрессВыполнения"];
			Если ПрогрессВыполнения = Неопределено Тогда
				СтатистикаВыполнения.Вставить("ПрогрессВыполнения", Параметры.ПрогрессВыполнения);
			Иначе
				ОбработаноОбъектов = Параметры.ПрогрессВыполнения.ОбработаноОбъектов;
				ПрогрессВыполнения.ОбработаноОбъектов = ПрогрессВыполнения.ОбработаноОбъектов + ОбработаноОбъектов;
			КонецЕсли;
		Иначе
			СтатистикаВыполнения.Вставить("ПрогрессВыполнения", Параметры.ПрогрессВыполнения);
		КонецЕсли;
		
		Если ДобавитьКоличествоЗапусков Тогда
			КоличествоЗапусковПроцедурыОбновления = КоличествоЗапусковПроцедурыОбновления(СтатистикаВыполнения) + 1;
		Иначе
			КоличествоЗапусковПроцедурыОбновления = КоличествоЗапусковПроцедурыОбновления(СтатистикаВыполнения);
		КонецЕсли;
		
		Если ЗначениеЗаполнено(ЧистаяДлительностьВыполнения) Тогда
			ДлительностьВыполнения = ЧистаяДлительностьВыполнения;
		Иначе
			ДлительностьВыполнения = ЗавершениеОбработкиДанных - КонтекстОбработчика.НачалоОбработкиДанных;
			Если СтатистикаВыполнения["ДлительностьВыполнения"] <> Неопределено Тогда
				ДлительностьВыполнения = ДлительностьВыполнения + СтатистикаВыполнения["ДлительностьВыполнения"];
			КонецЕсли;
		КонецЕсли;
		СтатистикаВыполнения.Вставить("ДлительностьВыполнения", ДлительностьВыполнения);
		СтатистикаВыполнения.Вставить("КоличествоЗапусков", КоличествоЗапусковПроцедурыОбновления);
		
		УстанавливаемыеСвойства.Вставить("СтатистикаВыполнения", Новый ХранилищеЗначения(СтатистикаВыполнения));
		УстанавливаемыеСвойства.Вставить("ДлительностьОбработки", ДлительностьВыполнения);
		УстановитьСвойстваОбработчика(ОбработчикОбновления.ИмяОбработчика, УстанавливаемыеСвойства);
	Исключение
		ОбработатьИсключениеОбработчика(КонтекстОбработчика, ОбработчикОбновления, ИнформацияОбОшибке());
	КонецПопытки;
	
КонецПроцедуры

// Завершение процедуры обработки данных.
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  ИмяОбработчика - Строка
//
Процедура ЗавершитьПроцедуруОбработкиДанных(КонтекстОбработчика, ИмяОбработчика)
	
	ОбработчикОбновления = ОбработчикОбновления(ИмяОбработчика);
	
	Параметры = КонтекстОбработчика.Параметры;
	
	ЗаписатьПрогрессВыполненияОбработчика(КонтекстОбработчика.ПараметрыОбработчикаОбновления);
	
	// Если обработчик обновления передал параметры, их нужно сохранить.
	ПереданныеПараметры = ПереданныеПараметрыОбработчикаОбновления(Параметры);
	СтатистикаВыполнения = ОбработчикОбновления.СтатистикаВыполнения.Получить();
	СтатистикаВыполнения.Вставить("ПараметрыОбработчика", ПереданныеПараметры);
	УстановитьСвойствоОбработчика(ОбработчикОбновления.ИмяОбработчика,
		"СтатистикаВыполнения",
		Новый ХранилищеЗначения(СтатистикаВыполнения));
	
	Если КонтекстОбработчика.ЕстьНеЗакрытыеТранзакции Тогда
		// В случае нахождения вложенной транзакции обработчик обновления повторно не вызывается.
		ОбработчикОбновления.Статус = Перечисления.СтатусыОбработчиковОбновления.Ошибка;
		ОбработчикОбновления.ИнформацияОбОшибке = Строка(ОбработчикОбновления.ИнформацияОбОшибке)
			+ Символы.ПС + КонтекстОбработчика.ИнформацияОбОшибке;
		
		ОбработчикОбновления.ЧислоПопыток = МаксимумПопытокОбновления(ОбработчикОбновления);
		
		Свойства = Новый Структура;
		Свойства.Вставить("ЧислоПопыток", ОбработчикОбновления.ЧислоПопыток);
		Свойства.Вставить("Статус", ОбработчикОбновления.Статус);
		Свойства.Вставить("ИнформацияОбОшибке", ОбработчикОбновления.ИнформацияОбОшибке);
		УстановитьСвойстваОбработчика(ОбработчикОбновления.ИмяОбработчика, Свойства);
	КонецЕсли;
	
	Если КонтекстОбработчика.ЗаписыватьВЖурнал Тогда
		ЗаписатьДетальнуюИнформациюОХодеОбновления(КонтекстОбработчика.ДетальноеОписаниеОбработчика);
	КонецЕсли;
	
КонецПроцедуры

Процедура ЗаполнитьБлокируемыеОбъекты(СтрокаВерсия, СведенияОБлокируемыхОбъектах)
	
	Для Каждого Обработчик Из СтрокаВерсия.Строки Цикл
		ПроцедураПроверки  = Обработчик.ПроцедураПроверки;
		БлокируемыеОбъекты = Обработчик.БлокируемыеОбъекты;
		Если ЗначениеЗаполнено(ПроцедураПроверки) И ЗначениеЗаполнено(БлокируемыеОбъекты) Тогда
			СвойстваОбработчика = Новый Структура;
			СвойстваОбработчика.Вставить("Выполнен", Ложь);
			СвойстваОбработчика.Вставить("ПроцедураПроверки", ПроцедураПроверки);
			
			СведенияОБлокируемыхОбъектах.Обработчики.Вставить(Обработчик.ИмяОбработчика, СвойстваОбработчика);
			МассивБлокируемыхОбъектов = СтрРазделить(БлокируемыеОбъекты, ",");
			Для Каждого БлокируемыйОбъект Из МассивБлокируемыхОбъектов Цикл
				БлокируемыйОбъект = СтрЗаменить(СокрЛП(БлокируемыйОбъект), ".", "");
				ИнформацияОбОбъекте = СведенияОБлокируемыхОбъектах.БлокируемыеОбъекты[БлокируемыйОбъект];
				Если ИнформацияОбОбъекте = Неопределено Тогда
					МассивОбработчиков = Новый Массив;
					МассивОбработчиков.Добавить(Обработчик.ИмяОбработчика);
					СведенияОБлокируемыхОбъектах.БлокируемыеОбъекты.Вставить(БлокируемыйОбъект, МассивОбработчиков);
				Иначе
					СведенияОБлокируемомОбъекте = СведенияОБлокируемыхОбъектах.БлокируемыеОбъекты[БлокируемыйОбъект]; // Массив
					СведенияОБлокируемомОбъекте.Добавить(Обработчик.ИмяОбработчика);
				КонецЕсли;
			КонецЦикла;
		ИначеЕсли ЗначениеЗаполнено(БлокируемыеОбъекты) И Не ЗначениеЗаполнено(ПроцедураПроверки) Тогда
			ТекстИсключения = НСтр("ru = 'У отложенного обработчика обновления ""%1""
				|заполнен список блокируемых объектов, но не задано свойство ""%2"".'");
			
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстИсключения, Обработчик.ИмяОбработчика, "ПроцедураПроверки");
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

Функция НовыеСведенияОБлокируемыхОбъектах(СтарыеСведения = Неопределено)
	
	СведенияОБлокируемыхОбъектах = Новый Структура;
	СведенияОБлокируемыхОбъектах.Вставить("БлокируемыеОбъекты", Новый Соответствие);
	СведенияОБлокируемыхОбъектах.Вставить("Обработчики", Новый Соответствие);
	СведенияОБлокируемыхОбъектах.Вставить("РазблокированныеОбъекты", Новый Соответствие);
	
	Если ТипЗнч(СтарыеСведения) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(СведенияОБлокируемыхОбъектах, СтарыеСведения);
	КонецЕсли;
	
	Возврат СведенияОБлокируемыхОбъектах;
	
КонецФункции

Процедура ЗаписатьСведенияОБлокируемыхОбъектах(Сведения) Экспорт
	
	Если Сведения = Неопределено Тогда
		НовоеЗначение = НовыеСведенияОБлокируемыхОбъектах();
	Иначе
		НовоеЗначение = Сведения;
	КонецЕсли;
	
	МенеджерКонстанты = Константы.СведенияОБлокируемыхОбъектах.СоздатьМенеджерЗначения();
	МенеджерКонстанты.Значение = Новый ХранилищеЗначения(НовоеЗначение);
	ОбновлениеИнформационнойБазы.ЗаписатьДанные(МенеджерКонстанты);
	
КонецПроцедуры

Процедура ЗаполнитьДанныеДляПараллельногоОтложенногоОбновления(Параметры) Экспорт
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		ОтметитьРегистрациюОтложенныхОбработчиковОбновления();
		Возврат;
	КонецЕсли;
	
	Если Параметры.ПриЗапускеКлиентскогоПриложения
		И Параметры.РежимВыполненияОтложенногоОбновления = "Отложенно" Тогда
		КлиентСервер  = Не ОбщегоНазначения.ИнформационнаяБазаФайловая();
		Коробка       = Не ОбщегоНазначения.РазделениеВключено();
		
		Если КлиентСервер И Коробка Тогда
			// Регистрация данных выполняется позже.
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	ПерезапускОбновления      = Параметры.Свойство("ПерезапускОбновления");
	РегистрируемыеОбработчики = Новый Массив;
	Если Параметры.Свойство("РегистрируемыеОбработчики") Тогда
		РегистрируемыеОбработчики = Параметры.РегистрируемыеОбработчики;
	КонецЕсли;
	
	УдалитьВсеПотокиОбновления();
	
	Если Не (СтандартныеПодсистемыПовтИсп.ИспользуетсяРИБ("СФильтром") И ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ()) И Не ПерезапускОбновления Тогда
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбновлениеИнформационнойБазы.Ссылка КАК Узел
		|ИЗ
		|	ПланОбмена.ОбновлениеИнформационнойБазы КАК ОбновлениеИнформационнойБазы
		|ГДЕ
		|	НЕ ОбновлениеИнформационнойБазы.ЭтотУзел";
		
		Выгрузка = Запрос.Выполнить().Выгрузить();
		УзлыОбмена = Выгрузка.ВыгрузитьКолонку("Узел");
		
		Для Каждого ЭлементСостава Из Метаданные.ПланыОбмена.ОбновлениеИнформационнойБазы.Состав Цикл
			ПланыОбмена.УдалитьРегистрациюИзменений(УзлыОбмена, ЭлементСостава.Метаданные);
		КонецЦикла;
	КонецЕсли;
	
	СведенияОбОбновлении = СведенияОбОбновленииИнформационнойБазы();
	ВерсииПодсистемНаНачалоОбновления = СведенияОбОбновлении.ВерсииПодсистемНаНачалоОбновления;
	
	Если Не ОбщегоНазначения.ЭтоПодчиненныйУзелРИБ()
		И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
		МодульОбменДаннымиСервер.СброситьЗначениеКонстантыСИзменениямиДляПодчиненногоУзлаРИБСФильтрами();
	КонецЕсли;
	
	ПараметрыИнициализированы = Ложь;
	
	Обработчики = ОбработчикиДляОтложеннойРегистрацииДанных(Ложь, ПерезапускОбновления, РегистрируемыеОбработчики);
	
	Для Каждого Обработчик Из Обработчики Цикл
		
		ВерсияПодсистемыНаНачалоОбновления = ВерсииПодсистемНаНачалоОбновления[Обработчик.ИмяБиблиотеки];
		
		Если Не ПараметрыИнициализированы Тогда
			
			ПараметрыОбработчикаСтруктура = ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке();
			ПараметрыИнициализированы = Истина;
			
			Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
				МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
				МодульОбменДаннымиСервер.ИнициализироватьФайлСДаннымиОбновления(ПараметрыОбработчикаСтруктура);
			КонецЕсли;
			
		КонецЕсли;
		
		ПараметрыОбработчикаСтруктура.Очередь = Обработчик.ОчередьОтложеннойОбработки;
		ПараметрыОбработчикаСтруктура.ИмяОбработчика = Обработчик.ИмяОбработчика;
		ПараметрыОбработчикаСтруктура.Вставить("ДанныеОбработчика", Новый Соответствие);
		ПараметрыОбработчикаСтруктура.Вставить("ПерезапускОбновления", ПерезапускОбновления);
		ПараметрыОбработчикаСтруктура.Вставить("АктуальныеДанные", ОбновлениеИнформационнойБазы.ПараметрыВыборкиАктуальныхДанных());
		ПараметрыОбработчикаСтруктура.Вставить("ЗарегистрированныеТаблицыРегистраторов", Новый Соответствие); 
		ПараметрыОбработчикаСтруктура.Вставить("ВерсияПодсистемыНаНачалоОбновления", ВерсияПодсистемыНаНачалоОбновления);
		
		Если Обработчик.Многопоточный Тогда
			ПараметрыОбработчикаСтруктура.ПараметрыВыборки =
				ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки();
		Иначе
			ПараметрыОбработчикаСтруктура.ПараметрыВыборки = Неопределено;
		КонецЕсли;
		
		ПараметрыОбработчика = Новый Массив;
		ПараметрыОбработчика.Добавить(ПараметрыОбработчикаСтруктура);
		Попытка
			Сообщение = НСтр("ru = 'Выполняется процедура заполнения данных
				                   |""%1""
				                   |отложенного обработчика обновления
				                   |""%2"".'");
			Сообщение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Сообщение,
				Обработчик.ПроцедураЗаполненияДанныхОбновления,
				Обработчик.ИмяОбработчика);
			ЗаписатьИнформацию(Сообщение);
			
			НачалоРегистрации = ТекущаяУниверсальнаяДатаВМиллисекундах();
			ОбщегоНазначения.ВыполнитьМетодКонфигурации(Обработчик.ПроцедураЗаполненияДанныхОбновления, ПараметрыОбработчика);
			КонецРегистрации  = ТекущаяУниверсальнаяДатаВМиллисекундах();
			
			ДлительностьРегистрации = (КонецРегистрации - НачалоРегистрации) / 1000; 
			
			Если Обработчик.Многопоточный Тогда
				ИсправитьПолныеИменаВПараметрахВыборки(ПараметрыОбработчикаСтруктура.ПараметрыВыборки);
			КонецЕсли;
			
			// Запись прогресса обновления.
			Если Не ПерезапускОбновления И Параметры.ВФоне Тогда
				ХодВыполненияОбработчиков = Параметры.ХодВыполненияОбработчиков;
				ХодВыполненияОбработчиков.ВыполненоОбработчиков = ХодВыполненияОбработчиков.ВыполненоОбработчиков + 1;
				Прогресс = 10 + ХодВыполненияОбработчиков.ВыполненоОбработчиков / ХодВыполненияОбработчиков.ВсегоОбработчиков * 90;
				ДлительныеОперации.СообщитьПрогресс(Прогресс);
			КонецЕсли;
		Исключение
			Если Не ПерезапускОбновления Тогда
				ОтметитьРегистрациюОтложенныхОбработчиковОбновления(Обработчик.ИмяБиблиотеки, Ложь);
			КонецЕсли;
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'При вызове процедуры заполнения данных
						   |""%1""
						   |отложенного обработчика обновления
						   |""%2""
						   |произошла ошибка:
						   |""%3"".'"),
				Обработчик.ПроцедураЗаполненияДанныхОбновления,
				Обработчик.ИмяОбработчика,
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			
			ЗаписатьОшибку(ТекстОшибки);

			Свойства = Новый Структура;
			Свойства.Вставить("Статус", Перечисления.СтатусыОбработчиковОбновления.Ошибка);
			Свойства.Вставить("ИнформацияОбОшибке", ТекстОшибки);
			УстановитьСвойстваОбработчика(Обработчик.ИмяОбработчика, Свойства);

			ВызватьИсключение;
		КонецПопытки;
		
		ОписаниеОбрабатываемыхДанных = НовоеОписаниеОбрабатываемыхДанных(Обработчик.Многопоточный);
		ОписаниеОбрабатываемыхДанных.ДанныеОбработчика = ПараметрыОбработчикаСтруктура.ДанныеОбработчика;
		ОписаниеОбрабатываемыхДанных.ИмяОбработчика    = Обработчик.ИмяОбработчика;
		ОписаниеОбрабатываемыхДанных.АктуальныеДанные  = ПараметрыОбработчикаСтруктура.АктуальныеДанные;
		Если ЗначениеЗаполнено(ПараметрыОбработчикаСтруктура.ЗарегистрированныеТаблицыРегистраторов) Тогда
			ЗарегистрированныеТаблицы = Новый Массив;
			Для Каждого КлючИЗначение Из ПараметрыОбработчикаСтруктура.ЗарегистрированныеТаблицыРегистраторов Цикл
				ЗарегистрированныеТаблицы.Добавить(КлючИЗначение.Значение);
			КонецЦикла;
		КонецЕсли;
		ОписаниеОбрабатываемыхДанных.ЗарегистрированныеТаблицыРегистраторов = ЗарегистрированныеТаблицы;
		
		Если Обработчик.Многопоточный Тогда
			ОписаниеОбрабатываемыхДанных.ПараметрыВыборки = ПараметрыОбработчикаСтруктура.ПараметрыВыборки;
		КонецЕсли;
		
		ОписаниеОбрабатываемыхДанных = Новый ХранилищеЗначения(ОписаниеОбрабатываемыхДанных, Новый СжатиеДанных(9));
		
		УстанавливаемыеСвойства = Новый Структура;
		УстанавливаемыеСвойства.Вставить("ОбрабатываемыеДанные", ОписаниеОбрабатываемыхДанных);
		УстанавливаемыеСвойства.Вставить("ДлительностьРегистрацииДанных", ДлительностьРегистрации);
		Если УказанФильтрАктуальныхДанных(ПараметрыОбработчикаСтруктура.АктуальныеДанные) Тогда
			УстанавливаемыеСвойства.Вставить("ИспользуетсяРазделениеПоКритичности", Истина);
		КонецЕсли;
		
		УстановитьСвойстваОбработчика(Обработчик.ИмяОбработчика, УстанавливаемыеСвойства);
	КонецЦикла;
	
	Если Не ПерезапускОбновления Тогда
		ОтметитьРегистрациюОтложенныхОбработчиковОбновления();
	КонецЕсли;
	
	Если ПараметрыИнициализированы И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
		МодульОбменДаннымиСервер.ЗавершитьЗаписьФайлаСДаннымиОбновления(ПараметрыОбработчикаСтруктура);
	КонецЕсли;
	
КонецПроцедуры

// Заполнить данные для параллельного отложенного обновления в фоне, используя несколько потоков.
//
// Параметры:
//  ИдентификаторФормы - УникальныйИдентификатор - идентификатор формы, отображающей процесс обновления.
//  АдресРезультата - Строка - адрес временного хранилища с результатом выполнения процедуры.
//
Процедура ЗапускРегистрацииДанныхОтложенногоОбновления(ИдентификаторФормы, АдресРезультата) Экспорт
	
	Группы = НовоеОписаниеГруппПотоковРегистрацииДанныхОтложенногоОбновления();
	
	Обработчики = ОбработчикиДляОтложеннойРегистрацииДанных();
	УдалитьВсеПотокиОбновления();
	
	Попытка
		ТекущаяОчередь = ?(Обработчики.Количество() > 0, Обработчики[0].ОчередьОтложеннойОбработки, 0);
		Для Каждого Обработчик Из Обработчики Цикл
			Если Обработчик.ОчередьОтложеннойОбработки > ТекущаяОчередь Тогда
				ОжидатьЗавершениеВсехПотоков(Группы); // @skip-check query-in-loop - пакетная регистрация данных.
				ТекущаяОчередь = Обработчик.ОчередьОтложеннойОбработки;
			КонецЕсли;
			
			ОписаниеОбрабатываемыхДанных = Обработчик.ОбрабатываемыеДанные.Получить();
			
			Поток = ДобавитьПотокРегистрацииДанныхОтложенногоОбновления(ОписаниеОбрабатываемыхДанных);
			ВыполнитьПоток(Группы, Поток, ИдентификаторФормы);
			ОжидатьСвободныйПоток(Группы); // @skip-check query-in-loop - пакетная регистрация данных.
		КонецЦикла;
		
		ОжидатьЗавершениеВсехПотоков(Группы);
		УдалитьВсеПотокиОбновления();
	Исключение
		ОтменитьВыполнениеВсехПотоков(Группы);
		УдалитьВсеПотокиОбновления();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Заполнение данных отложенного обработчика в фоновом задании.
//
// Параметры:
//  ОписаниеОбрабатываемыхДанных - см. НовоеОписаниеОбрабатываемыхДанных
//  АдресРезультата - Строка - адрес временного хранилища для сохранения результата выполнения процедуры.
//
Процедура ЗаполнитьДанныеОтложенногоОбработчика(ОписаниеОбрабатываемыхДанных, АдресРезультата) Экспорт
	
	ПараметрыОтметкиКОбработке = ОбновлениеИнформационнойБазы.ОсновныеПараметрыОтметкиКОбработке();
	ЕстьПодсистемаОбменаДанными = ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными");
	
	Если ЕстьПодсистемаОбменаДанными Тогда
		МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
		МодульОбменДаннымиСервер.ИнициализироватьФайлСДаннымиОбновления(ПараметрыОтметкиКОбработке);
	КонецЕсли;
	
	ПараметрыОтметкиКОбработке.Очередь = ОписаниеОбрабатываемыхДанных.Очередь;
	ПараметрыОтметкиКОбработке.ИмяОбработчика = ОписаниеОбрабатываемыхДанных.ИмяОбработчика;
	ПараметрыОтметкиКОбработке.ВерсияПодсистемыНаНачалоОбновления = ОписаниеОбрабатываемыхДанных.ВерсияПодсистемыНаНачалоОбновления;
	ПараметрыОтметкиКОбработке.Вставить("ДанныеОбработчика", Новый Соответствие);
	МногопоточныйРежим = ЭтоОписаниеДанныхМногопоточногоОбработчика(ОписаниеОбрабатываемыхДанных);
	
	Если МногопоточныйРежим Тогда
		ПараметрыОтметкиКОбработке.ПараметрыВыборки =
			ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки();
	Иначе
		ПараметрыОтметкиКОбработке.ПараметрыВыборки = Неопределено;
	КонецЕсли;
	
	ПараметрыОбработчика = Новый Массив;
	ПараметрыОбработчика.Добавить(ПараметрыОтметкиКОбработке);
	
	ШаблонСообщения = НСтр(
		"ru = 'Выполняется процедура заполнения данных
		|""%1""
		|отложенного обработчика обновления
		|""%2"".'");
	ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения,
		ОписаниеОбрабатываемыхДанных.ПроцедураЗаполнения,
		ОписаниеОбрабатываемыхДанных.ИмяОбработчика);
	ЗаписатьИнформацию(ТекстСообщения);
	
	Попытка
		НачалоРегистрации = ТекущаяУниверсальнаяДатаВМиллисекундах();
		ОбщегоНазначения.ВыполнитьМетодКонфигурации(ОписаниеОбрабатываемыхДанных.ПроцедураЗаполнения, ПараметрыОбработчика);
		КонецРегистрации  = ТекущаяУниверсальнаяДатаВМиллисекундах();
	Исключение
		ИнформацияОбОшибке = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		ШаблонОшибки = НСтр(
			"ru = 'При вызове процедуры заполнения данных
			|""%1""
			|отложенного обработчика обновления
			|""%2""
			|произошла ошибка:
			|""%3"".'");
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
			ОписаниеОбрабатываемыхДанных.ПроцедураЗаполнения,
			ОписаниеОбрабатываемыхДанных.ИмяОбработчика,
			ИнформацияОбОшибке);
		ЗаписатьОшибку(ТекстОшибки);

		Свойства = Новый Структура;
		Свойства.Вставить("Статус", Перечисления.СтатусыОбработчиковОбновления.Ошибка);
		Свойства.Вставить("ИнформацияОбОшибке", ТекстОшибки);
		УстановитьСвойстваОбработчика(ОписаниеОбрабатываемыхДанных.ИмяОбработчика, Свойства);

		ВызватьИсключение;
	КонецПопытки;
	
	ДлительностьРегистрацииДанных = (КонецРегистрации - НачалоРегистрации) / 1000;
	
	Результат = Новый Структура;
	Результат.Вставить("ДанныеОбработчика", ПараметрыОтметкиКОбработке.ДанныеОбработчика);
	Результат.Вставить("ДлительностьРегистрацииДанных", ДлительностьРегистрацииДанных);
	Результат.Вставить("АктуальныеДанные", ПараметрыОтметкиКОбработке.АктуальныеДанные);
	Результат.Вставить("ЗарегистрированныеТаблицыРегистраторов", ПараметрыОтметкиКОбработке.ЗарегистрированныеТаблицыРегистраторов);
	
	Если МногопоточныйРежим Тогда
		Результат.Вставить("ПараметрыВыборки", ПараметрыОтметкиКОбработке.ПараметрыВыборки);
	КонецЕсли;
	
	Если ЕстьПодсистемаОбменаДанными Тогда
		ДанныеОбновления = МодульОбменДаннымиСервер.ЗавершитьЗаписьФайлаИПолучитьДанныеОбновления(ПараметрыОтметкиКОбработке);
		Результат.Вставить("ДанныеОбновления", ДанныеОбновления);
		Результат.Вставить("ИмяФайлаСИзменениями", ПараметрыОтметкиКОбработке.ИмяФайлаСИзменениями);
	КонецЕсли;
	
	ПоместитьВоВременноеХранилище(Результат, АдресРезультата);
	
КонецПроцедуры

Процедура УстановитьПараметрыОбработчикаОбновления(ОбработчикОбновления, Отложенно = Ложь, Параллельно = Ложь)
	
	Если ОбработчикОбновления = Неопределено Тогда
		ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(НовыеПараметрыОбработчикаОбновления());
		Возврат;
	КонецЕсли;
	
	Если Отложенно Тогда
		РежимВыполнения = "Отложенно";
		ИмяОбработчика = ОбработчикОбновления.ИмяОбработчика;
	Иначе
		РежимВыполнения = "Монопольно";
		ИмяОбработчика = ОбработчикОбновления.Процедура;
	КонецЕсли;
	
	Если Параллельно Тогда
		РежимВыполненияОтложенныхОбработчиков = "Параллельно";
	Иначе
		РежимВыполненияОтложенныхОбработчиков = "Последовательно";
	КонецЕсли;
	
	ПолучениеСвойств = Новый Структура;
	ПолучениеСвойств.Вставить("ИзменяемыеОбъекты", "");
	ЗаполнитьЗначенияСвойств(ПолучениеСвойств, ОбработчикОбновления);
	
	ПараметрыОбработчикаОбновления = НовыеПараметрыОбработчикаОбновления();
	ПараметрыОбработчикаОбновления.ЗапускатьТолькоВГлавномУзле = ОбработчикОбновления.ЗапускатьТолькоВГлавномУзле;
	ПараметрыОбработчикаОбновления.ЗапускатьИВПодчиненномУзлеРИБСФильтрами = ОбработчикОбновления.ЗапускатьИВПодчиненномУзлеРИБСФильтрами;
	ПараметрыОбработчикаОбновления.ОчередьОтложеннойОбработки = ОбработчикОбновления.ОчередьОтложеннойОбработки;
	ПараметрыОбработчикаОбновления.РежимВыполнения = РежимВыполнения;
	ПараметрыОбработчикаОбновления.РежимВыполненияОтложенныхОбработчиков = РежимВыполненияОтложенныхОбработчиков;
	ПараметрыОбработчикаОбновления.КлючЗаписиПрогрессаОбновления = Новый УникальныйИдентификатор;
	ПараметрыОбработчикаОбновления.ЕстьОбработанныеОбъекты = Ложь;
	ПараметрыОбработчикаОбновления.ИмяОбработчика = ИмяОбработчика;
	ПараметрыОбработчикаОбновления.ИзменяемыеОбъекты = УникальныеЗначенияЧерезЗапятую(ПолучениеСвойств.ИзменяемыеОбъекты);
	Если Параллельно Тогда
		ОбрабатываемыеДанные = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
		Если ТипЗнч(ОбрабатываемыеДанные) = Тип("Структура")
			И ОбрабатываемыеДанные.Свойство("АктуальныеДанные")
			И ТипЗнч(ОбрабатываемыеДанные.АктуальныеДанные) = Тип("Структура") Тогда
			ОбрабатываемыеДанные.АктуальныеДанные.ВидСравнения = ВидСравненияСтрокой(ОбрабатываемыеДанные.АктуальныеДанные.ВидСравнения, ИмяОбработчика);
			ПараметрыОбработчикаОбновления.АктуальныеДанные = Новый ФиксированнаяСтруктура(ОбрабатываемыеДанные.АктуальныеДанные);
		КонецЕсли;
	КонецЕсли;
	
	ПараметрыСеанса.ПараметрыОбработчикаОбновления = Новый ФиксированнаяСтруктура(ПараметрыОбработчикаОбновления);
	
КонецПроцедуры

Функция НовыеПараметрыОбработчикаОбновления() Экспорт
	ПараметрыОбработчикаОбновления = Новый Структура;
	ПараметрыОбработчикаОбновления.Вставить("ЗапускатьТолькоВГлавномУзле", Ложь);
	ПараметрыОбработчикаОбновления.Вставить("ЗапускатьИВПодчиненномУзлеРИБСФильтрами", Ложь);
	ПараметрыОбработчикаОбновления.Вставить("ОчередьОтложеннойОбработки", 0);
	ПараметрыОбработчикаОбновления.Вставить("РежимВыполнения", "");
	ПараметрыОбработчикаОбновления.Вставить("РежимВыполненияОтложенныхОбработчиков", "");
	ПараметрыОбработчикаОбновления.Вставить("ЕстьОбработанныеОбъекты", Ложь);
	ПараметрыОбработчикаОбновления.Вставить("КлючЗаписиПрогрессаОбновления", Неопределено);
	ПараметрыОбработчикаОбновления.Вставить("ИмяОбработчика", "");
	ПараметрыОбработчикаОбновления.Вставить("ИзменяемыеОбъекты", "");
	ПараметрыОбработчикаОбновления.Вставить("ИдентификаторТранзакции", "");
	ПараметрыОбработчикаОбновления.Вставить("АктуальныеДанные", Неопределено);
	ПараметрыОбработчикаОбновления.Вставить("ОбработаныАктуальныеДанные", Неопределено);
	ПараметрыОбработчикаОбновления.Вставить("ОбработанныеТаблицыРегистраторов", Неопределено);
	ПараметрыОбработчикаОбновления.Вставить("ДатаПоследнейЗаписиПрогресса", Неопределено);
	ПараметрыОбработчикаОбновления.Вставить("ОбработаноОбъектов", 0);
	
	Возврат ПараметрыОбработчикаОбновления;
КонецФункции

Функция ВидСравненияСтрокой(Знач УсловиеСравнения, ИмяОбработчика)
	
	Если Не ЗначениеЗаполнено(УсловиеСравнения) Тогда
		Возврат "";
	ИначеЕсли УсловиеСравнения = ВидСравнения.Больше Тогда
		УсловиеСравнения = ">";
	ИначеЕсли УсловиеСравнения = ВидСравнения.БольшеИлиРавно Тогда
		УсловиеСравнения = ">=";
	ИначеЕсли УсловиеСравнения = ВидСравнения.Меньше Тогда
		УсловиеСравнения = "<";
	ИначеЕсли УсловиеСравнения = ВидСравнения.МеньшеИлиРавно Тогда
		УсловиеСравнения = "<=";
	ИначеЕсли УсловиеСравнения = ВидСравнения.Равно Тогда
		УсловиеСравнения = "=";
	ИначеЕсли УсловиеСравнения = ВидСравнения.НеРавно Тогда
		УсловиеСравнения = "<>";
	Иначе
		ТекстОшибки = НСтр("ru = 'Указан неподдерживаемый вид сравнения ""%1"" для фильтра актуальных данных
			|в процедуре регистрации данных обработчика ""%2"".
			|Доступные варианты описаны в функции %3'");
		ДоступныеВидыСравнения = "ОбновлениеИнформационнойБазы.ПараметрыВыборкиАктуальныхДанных";
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстОшибки,
			УсловиеСравнения, ИмяОбработчика, ДоступныеВидыСравнения);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Возврат УсловиеСравнения;
	
КонецФункции

Функция ФильтрАктуальныхДанных(ПараметрыОбработчика, ПсевдонимТаблицы) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("Условие", "ИСТИНА");
	Результат.Вставить("Значение", Неопределено);
	Результат.Вставить("ЕстьФильтр", Ложь);
	
	Если ПараметрыОбработчика = Неопределено Тогда
		Параметры = ПараметрыСеанса.ПараметрыОбработчикаОбновления;
		ФильтрАктуальныеДанные = Параметры.АктуальныеДанные;
	Иначе
		ФильтрАктуальныеДанные = ПараметрыОбработчика.АктуальныеДанные;
	КонецЕсли;
	
	
	Если ФильтрАктуальныеДанные = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	Если Не УказанФильтрАктуальныхДанных(ФильтрАктуальныеДанные) Тогда
		Возврат Результат;
	КонецЕсли;
	
	ПорядокОбрабатываемыхДанных = ТекущийПорядокОбновления();
	Если ПорядокОбрабатываемыхДанных = Перечисления.ПорядокОбработчиковОбновления.Некритичный Тогда
		Возврат Результат;
	КонецЕсли;
	
	УсловиеСравнения = ФильтрАктуальныеДанные.ВидСравнения;
	ШаблонУсловия = "%1.%2 %3 &ЗначениеОтбораАктуальныхДанных";
	
	Условие = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		ШаблонУсловия,
		ПсевдонимТаблицы,
		ФильтрАктуальныеДанные.ПолеОтбора,
		УсловиеСравнения);
	
	Результат.Условие  = Условие;
	Результат.Значение = ФильтрАктуальныеДанные.Значение;
	Результат.ЕстьФильтр = Истина;
	
	Возврат Результат;
	
КонецФункции

Функция УказанФильтрАктуальныхДанных(ФильтрАктуальныеДанные)
	
	Если (ТипЗнч(ФильтрАктуальныеДанные) = Тип("Структура") Или ТипЗнч(ФильтрАктуальныеДанные) = Тип("ФиксированнаяСтруктура"))
		И ЗначениеЗаполнено(ФильтрАктуальныеДанные.ПолеОтбора)
		И ЗначениеЗаполнено(ФильтрАктуальныеДанные.ВидСравнения) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Обработать исключение, возникшее при подготовке или завершении выполнения обработчика в основном потоке.
//
// Параметры:
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  ОбработчикОбновления - СтрокаДереваЗначений - обработчик обновления в виде строки дерева обработчиков.
//
Процедура ОбработатьИсключениеОбработчика(КонтекстОбработчика, ОбработчикОбновления, ИнформацияОбОшибке)
	
	Если КонтекстОбработчика.ЗаписыватьВЖурнал Тогда
		ЗаписатьДетальнуюИнформациюОХодеОбновления(КонтекстОбработчика.ДетальноеОписаниеОбработчика);
	КонецЕсли;
	
	// АПК:325-выкл откат незакрытых транзакций после выполнения обработчика.
	Если Не КонтекстОбработчика.Свойство("ПропуститьОтменуТранзакций") Тогда
		Пока ТранзакцияАктивна() Цикл
			ОтменитьТранзакцию();
		КонецЦикла;
	КонецЕсли;
	// АПК:325-вкл
	
	ДобавляемоеЧислоПопыток = ДобавляемоеЧислоПопыток(ОбработчикОбновления, КонтекстОбработчика, Истина);
	ЧислоПопыток = ОбработчикОбновления.ЧислоПопыток + ДобавляемоеЧислоПопыток;
	ПодробноеПредставлениеОшибки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
	
	МаксимумПопытокОбновления = МаксимумПопытокОбновления(ОбработчикОбновления);
	
	Если ЧислоПопыток < МаксимумПопытокОбновления Тогда
		ЗаписатьПредупреждение(ПодробноеПредставлениеОшибки);
	Иначе
		ОбновлениеИнформационнойБазы.ЗаписатьСобытиеВЖурналРегистрации(ПодробноеПредставлениеОшибки, , КонтекстОбработчика.Параметры);
	КонецЕсли;
	
	Свойства = Новый Структура;
	Свойства.Вставить("ЧислоПопыток", ЧислоПопыток);
	Свойства.Вставить("Статус", Перечисления.СтатусыОбработчиковОбновления.Ошибка);
	Свойства.Вставить("ИнформацияОбОшибке", ПодробноеПредставлениеОшибки);
	УстановитьСвойстваОбработчика(ОбработчикОбновления.ИмяОбработчика, Свойства);
	
КонецПроцедуры

// Обработать СуществующиеПоставляемыеЭлементы, полученный в результате разбиения результата поиска данных для
// обновления, в отдельном потоке.
//
// Параметры:
//  СуществующиеПоставляемыеЭлементы - Массив из см. НовыйНаборПорцийДляОбновления
//  ОписаниеПотоков - см. НовоеОписаниеПотоков
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//
Процедура ОбработатьФрагментДанныхВПотоке(Фрагмент, Группы, КонтекстОбработчика)
	
	КонтекстОбработчикаДляПотока = ОбщегоНазначения.СкопироватьРекурсивно(КонтекстОбработчика); // см. НовыйКонтекстОбработчика
	КонтекстОбработчикаДляПотока.Параметры.ОбновляемыеДанные = Фрагмент;
	Поток = НовыйПоток();
	ДобавитьПотокОбработчикаОбновления(Поток, КонтекстОбработчикаДляПотока);
	ВыполнитьПоток(Группы, Поток);
	КонтекстОбработчикаДляПотока.Параметры.ОбновляемыеДанные.НаборДанных = Неопределено;
	
КонецПроцедуры

// Получить количество запусков процедуры обновления.
//
// Параметры:
//  ОбработчикОбновления - СтрокаДереваЗначений - обработчик обновления в виде строки дерева обработчиков.
//
// Возвращаемое значение:
//  Число - количество запусков.
//
Функция КоличествоЗапусковПроцедурыОбновления(СтатистикаВыполнения)
	
	КоличествоЗапусковПроцедурыОбновления = СтатистикаВыполнения["КоличествоЗапусков"];
	
	Если КоличествоЗапусковПроцедурыОбновления = Неопределено Тогда
		КоличествоЗапусковПроцедурыОбновления = 0;
	КонецЕсли;
	
	Возврат КоличествоЗапусковПроцедурыОбновления;
	
КонецФункции

// Возвращает максимальное количество попыток обновления для указанного обработчика обновления.
//
// Параметры:
//  ОбработчикОбновления - СтрокаТаблицыЗначений - обработчик обновления в виде строки дерева обработчиков.
//
// Возвращаемое значение:
//  Число - максимальное количество попыток обновления.
//
Функция МаксимумПопытокОбновления(ОбработчикОбновления)
	
	Если ОбработчикОбновления.Многопоточный Тогда
		ОбрабатываемыеДанные = ОбработчикОбновления.ОбрабатываемыеДанные.Получить();
		ПараметрыВыборки = ОбрабатываемыеДанные.ПараметрыВыборки;
		ПолныеИменаОбъектов = ПараметрыВыборки.ПолныеИменаОбъектов;
		ПолныеИменаРегистров = ПараметрыВыборки.ПолныеИменаРегистров;
		СоставОбъектов = СтрРазделить(ПолныеИменаОбъектов, ",");
		СоставРегистров = СтрРазделить(ПолныеИменаРегистров, ",");
		КоличествоПотоков = КоличествоПотоковОбновленияИнформационнойБазы();
		Множитель = СоставОбъектов.Количество() * СоставРегистров.Количество() + КоличествоПотоков;
	Иначе
		Множитель = 1;
	КонецЕсли;
	
	Возврат 3 * Множитель;
	
КонецФункции

// Количество добавляемых попыток для счетчика ЧислоПопыток.
//
// Параметры:
//  ОбработчикОбновления - СтрокаДереваЗначений - обработчик обновления в виде строки дерева обработчиков.
//  КонтекстОбработчика - см. НовыйКонтекстОбработчика
//  Ошибка - Булево - Истина, если в обработчике обновления возникла ошибка.
//
// Возвращаемое значение:
//  Число - 0, если это многопоточный обработчик, которому не были переданы данные для обновления, иначе 1.
//
Функция ДобавляемоеЧислоПопыток(ОбработчикОбновления, КонтекстОбработчика, Ошибка = Ложь)
	
	Если ОбработчикОбновления.Многопоточный Тогда
		Если КонтекстОбработчика.Параметры.Свойство("ОбновляемыеДанные") Тогда
			ОбновляемыеДанные = КонтекстОбработчика.Параметры.ОбновляемыеДанные;
		Иначе
			ОбновляемыеДанные = Неопределено;
		КонецЕсли;
		
		// Проверяется по полям ОбновляемыеДанные.ПерваяЗапись и ОбновляемыеДанные.ПоследняяЗапись, а не по полю
		// ОбновляемыеДанные.НаборДанных, т.к. оно очищается в ОбработатьФрагментДанныхВПотоке() для экономии памяти.
		// ОбновляемыеДанные может быть Неопределено, если обработчик выбросил исключение.
		Если ОбновляемыеДанные <> Неопределено Тогда
			ЕстьДанные = ОбновляемыеДанные.ПерваяЗапись <> Неопределено Или ОбновляемыеДанные.ПоследняяЗапись <> Неопределено;
			Если Не ЕстьДанные И Не Ошибка Тогда
				Возврат 0;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Возврат 1;
	
КонецФункции

Функция МинимальнаяОчередьОтложеннойОбработки()
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.УстановитьПараметр("РежимВыполненияОтложенногоОбработчика", Перечисления.РежимыВыполненияОтложенныхОбработчиков.Параллельно);
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ОбработчикиОбновления.ОчередьОтложеннойОбработки КАК Очередь,
		|	ОбработчикиОбновления.Многопоточный КАК Многопоточный,
		|	ОбработчикиОбновления.ОбрабатываемыеДанные КАК ОбрабатываемыеДанные,
		|	ОбработчикиОбновления.Статус КАК Статус,
		|	ОбработчикиОбновления.ЧислоПопыток КАК ЧислоПопыток
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|	И ОбработчикиОбновления.РежимВыполненияОтложенногоОбработчика = &РежимВыполненияОтложенногоОбработчика
		|	И ОбработчикиОбновления.Статус <> &Статус
		|
		|УПОРЯДОЧИТЬ ПО
		|	Очередь";
	Результат = Запрос.Выполнить().Выгрузить();
	Если Результат.Количество() = 0 Тогда
		Возврат 0;
	КонецЕсли;
	Для Каждого ОбработчикОбновления Из Результат Цикл
		Если ОбработчикОбновления.Статус <> Перечисления.СтатусыОбработчиковОбновления.Ошибка Тогда
			Прервать;
		КонецЕсли;
		МаксимумПопыток = МаксимумПопытокОбновления(ОбработчикОбновления) - 1;
		Если ОбработчикОбновления.ЧислоПопыток >= МаксимумПопыток Тогда
			Продолжить;
		Иначе
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат ОбработчикОбновления.Очередь;
	
КонецФункции

Функция ОстатокМассива(Массив, ЗначениеНачала)
	
	ОстатокМассива = Новый Массив;
	ТочноеЗначениеНачала = СокрЛП(ЗначениеНачала);
	ЗначениеНайдено = Ложь;
	
	Для Каждого Элемент Из СтрРазделитьСокрЛП(Массив, ",") Цикл
		Если ТочноеЗначениеНачала = Элемент Тогда
			ЗначениеНайдено = Истина;
		КонецЕсли;
		
		Если ЗначениеНайдено Тогда
			ОстатокМассива.Добавить(Элемент);
		КонецЕсли;
	КонецЦикла;
	
	Возврат ОстатокМассива;
	
КонецФункции

// Исправить перечисленные через запятую полные имена объектов и регистров
// с помощью УникальныеЗначенияЧерезЗапятую, чтобы не сбивалась курсорная выборка данных.
//
// Параметры:
//  ПараметрыВыборки - см. ОбновлениеИнформационнойБазы.ДополнительныеПараметрыВыборкиДанныхДляМногопоточнойОбработки
//
Процедура ИсправитьПолныеИменаВПараметрахВыборки(ПараметрыВыборки)
	
	ПараметрыВыборки.ПолныеИменаОбъектов = УникальныеЗначенияЧерезЗапятую(ПараметрыВыборки.ПолныеИменаОбъектов);
	ПараметрыВыборки.ПолныеИменаРегистров = УникальныеЗначенияЧерезЗапятую(ПараметрыВыборки.ПолныеИменаРегистров);
	
КонецПроцедуры

// Оставить только уникальные значения, перечисленные через запятую и упорядочить их.
//
// Параметры:
//  Строка - Строка - значения, перечисленные через запятую.
//
// Возвращаемое значение:
//  Строка - только уникальные упорядоченные значения через запятую.
//
Функция УникальныеЗначенияЧерезЗапятую(Строка)
	
	Если Строка = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Значения = СтрРазделить(Строка, ",");
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Значение");
	
	Для Каждого Значение Из Значения Цикл
		ТочноеЗначение = СокрЛП(Значение);
		
		Если Не ПустаяСтрока(ТочноеЗначение) Тогда
			СтрокаТаблицы = Таблица.Добавить();
			СтрокаТаблицы.Значение = ТочноеЗначение;
		КонецЕсли;
	КонецЦикла;
	
	Таблица.Свернуть("Значение");
	Таблица.Сортировать("Значение");
	Значения = Таблица.ВыгрузитьКолонку("Значение");
	
	Возврат СтрСоединить(Значения, ",");
	
КонецФункции

Функция РасшифровкаЯчейки(Статус, ЕстьОшибки, ПроблемаВДанных) Экспорт
	Массив = Новый Массив;
	Массив.Добавить(Статус);
	Массив.Добавить(ЕстьОшибки);
	Массив.Добавить(ПроблемаВДанных);
	Возврат Массив;
КонецФункции

Процедура СброситьПрогрессВыполненияОбработчиков()
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	Набор = РегистрыСведений.ПрогрессОбновления.СоздатьНаборЗаписей();
	Набор.Записать();
	
КонецПроцедуры

Процедура ДобавитьИнформациюОбОшибкеВОбработчике(ИмяОбработчика) Экспорт
	
	Если Не ЗначениеЗаполнено(ИмяОбработчика) Тогда
		ТекстОшибки = НСтр("ru = 'Не удалось сохранить информацию об ошибке в обработчике
			|по причине:
			|Возможно вызов выполняется не из обработчика обновления.'");
		ЗаписатьОшибку(ТекстОшибки);
		Возврат;
	КонецЕсли;
	
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.ОбработчикиОбновления");
	ЭлементБлокировкиДанных.УстановитьЗначение("ИмяОбработчика", ИмяОбработчика);
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать();
		
		МенеджерЗаписи = РегистрыСведений.ОбработчикиОбновления.СоздатьМенеджерЗаписи();
		МенеджерЗаписи.ИмяОбработчика = ИмяОбработчика;
		МенеджерЗаписи.Прочитать();
		
		СтатистикаВыполнения = МенеджерЗаписи.СтатистикаВыполнения;
		СтатистикаВыполнения = СтатистикаВыполнения.Получить();
		СтатистикаВыполнения.Вставить("ЕстьОшибки", Истина);
		МенеджерЗаписи.СтатистикаВыполнения = Новый ХранилищеЗначения(СтатистикаВыполнения);
		МенеджерЗаписи.Записать();
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ТекстОшибки = НСтр("ru = 'Не удалось сохранить информацию об ошибке в обработчике %1
			|по причине:
			|%2'");
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстОшибки,
			ИмяОбработчика, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
		ЗаписатьОшибку(ТекстОшибки);
	КонецПопытки
	
КонецПроцедуры

Процедура Пауза(Секунд) Экспорт
	
	ТекущийСеансИнформационнойБазы = ПолучитьТекущийСеансИнформационнойБазы();
	ФоновоеЗадание = ТекущийСеансИнформационнойБазы.ПолучитьФоновоеЗадание();
	
	Если ФоновоеЗадание = Неопределено Тогда
		Параметры = Новый Массив;
		Параметры.Добавить(Секунд);
		ФоновоеЗадание = ФоновыеЗадания.Выполнить("ОбновлениеИнформационнойБазыСлужебный.Пауза", Параметры);
	КонецЕсли;
	
	ФоновоеЗадание.ОжидатьЗавершенияВыполнения(Секунд);
	
КонецПроцедуры

Функция ДлительностьЭтапаСтрокой(Знач Длительность) Экспорт
	
	Если Длительность < 1 Тогда
		Возврат "< " + НСтр("ru = '1 сек.'");
	ИначеЕсли Длительность < 60 Тогда
		Шаблон = НСтр("ru = '%1 сек.'");
		Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Шаблон, Цел(Длительность));
	ИначеЕсли Длительность < 3600 Тогда
		Шаблон = НСтр("ru = '%1 мин. %2 сек.'");
		Длительность = Длительность / 60; // В минутах.
		Минуты = Цел(Длительность);
		Секунды = Цел((Длительность - Минуты) * 60);
		Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Шаблон, Минуты, Секунды);
	Иначе
		Шаблон = НСтр("ru = '%1 ч. %2 мин.'");
		Длительность = Длительность / 60 / 60; // В часах.
		Часы = Цел(Длительность);
		Минуты = Цел((Длительность - Часы) * 60);
		Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Шаблон, Часы, Минуты);
	КонецЕсли;
	
КонецФункции

Функция ТекущийПорядокОбновления()
	
	ПорядокОбновления = Неопределено;
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		ИБФайловая = ОбщегоНазначения.ИнформационнаяБазаФайловая();
		ПараметрЗапускаКлиента  = СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере().Получить("ПараметрЗапуска");
		ФорсированиеОтложенного = СтрНайти(НРег(ПараметрЗапускаКлиента), НРег("ВыполнитьОтложенноеОбновлениеСейчас")) > 0;
		Если ФорсированиеОтложенного Или ИБФайловая Тогда
			Возврат Перечисления.ПорядокОбработчиковОбновления.Некритичный;
		Иначе
			ПорядокОбновления = Константы.ПорядокОбрабатываемыхДанных.Получить();
		КонецЕсли;
	Иначе
		ПорядокОбновления = Константы.ПорядокОбрабатываемыхДанных.Получить();
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ПорядокОбновления) Тогда
		Возврат Перечисления.ПорядокОбработчиковОбновления.Критичный;
	Иначе
		Возврат ПорядокОбновления;
	КонецЕсли;
	
КонецФункции

Функция ОчередиДляОчистки(Обработанные)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РежимВыполнения", Перечисления.РежимыВыполненияОбработчиков.Отложенно);
	Запрос.УстановитьПараметр("Статус", Перечисления.СтатусыОбработчиковОбновления.Выполнен);
	Запрос.УстановитьПараметр("РежимВыполненияОтложенногоОбработчика", Перечисления.РежимыВыполненияОтложенныхОбработчиков.Параллельно);
	Запрос.УстановитьПараметр("Обработанные", Обработанные);
	Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ОбработчикиОбновления.ОчередьОтложеннойОбработки КАК Очередь
		|ИЗ
		|	РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновления
		|ГДЕ
		|	ОбработчикиОбновления.РежимВыполнения = &РежимВыполнения
		|	И ОбработчикиОбновления.РежимВыполненияОтложенногоОбработчика = &РежимВыполненияОтложенногоОбработчика
		|	И ОбработчикиОбновления.Статус = &Статус
		|	И НЕ ОбработчикиОбновления.ОчередьОтложеннойОбработки В (&Обработанные)
		|	И НЕ ИСТИНА В
		|			(ВЫБРАТЬ ПЕРВЫЕ 1
		|				ИСТИНА
		|			ИЗ
		|				РегистрСведений.ОбработчикиОбновления КАК ОбработчикиОбновленияПроверка
		|			ГДЕ
		|				ОбработчикиОбновленияПроверка.ОчередьОтложеннойОбработки = ОбработчикиОбновления.ОчередьОтложеннойОбработки
		|				И ОбработчикиОбновленияПроверка.Статус <> &Статус)";
	
	Результат = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Очередь");
	Возврат Результат;
	
КонецФункции

Процедура ЗаполнитьЧитаемыеИИзменяемыеДанные(Обработчик, ЧитаемыеИИзменяемыеТаблицы)
	ОчередьОбработчика = Обработчик.ОчередьОтложеннойОбработки;
	ТаблицыПоОчереди = ЧитаемыеИИзменяемыеТаблицы[ОчередьОбработчика];
	Если ТаблицыПоОчереди = Неопределено Тогда
		ТаблицыПоОчереди = Новый Массив;
	КонецЕсли;
	
	ЧитаемыеТаблицы   = СтрРазделить(Обработчик.ЧитаемыеОбъекты, ",", Ложь);
	ИзменяемыеТаблицы = СтрРазделить(Обработчик.ИзменяемыеОбъекты, ",", Ложь); 
	
	ИмяОбработчикаЧастями = СтрРазделить(Обработчик.ИмяОбработчика, ".");
	Если ИмяОбработчикаЧастями.Количество() > 2 Тогда
		Позиция      = СтрНайти(Обработчик.ИмяОбработчика, ".", НаправлениеПоиска.СКонца);
		ИмяМенеджера = Лев(Обработчик.ИмяОбработчика, Позиция - 1);
		ПолноеИмяОбъекта = ИмяОбъектаМетаданныхПоИмениМенеджера(ИмяМенеджера); 
		Если ТаблицыПоОчереди.Найти(ПолноеИмяОбъекта) = Неопределено Тогда
			ТаблицыПоОчереди.Добавить(ПолноеИмяОбъекта);
		КонецЕсли;
	КонецЕсли;
	
	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ТаблицыПоОчереди, ЧитаемыеТаблицы, Истина);
	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ТаблицыПоОчереди, ИзменяемыеТаблицы, Истина);
	
	ЧитаемыеИИзменяемыеТаблицы.Вставить(ОчередьОбработчика, ТаблицыПоОчереди);
КонецПроцедуры

Процедура ОчиститьОбработанныеОчереди(ОчередиДляОчистки, Обработанные, СведенияОбОбновлении)
	
	Если ОчередиДляОчистки.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ЧитаемыеИИзменяемыеТаблицы = СведенияОбОбновлении.ЧитаемыеИИзменяемыеТаблицы;
	ВсеЧитаемыеИИзменяемыеТаблицы = Новый Массив;
	Для Каждого КлючИЗначение Из ЧитаемыеИИзменяемыеТаблицы Цикл
		ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ВсеЧитаемыеИИзменяемыеТаблицы, КлючИЗначение.Значение, Истина);
	КонецЦикла;
	
	Для Каждого ОчередьДляОчистки Из ОчередиДляОчистки Цикл
		ОбъектыОчереди = ЧитаемыеИИзменяемыеТаблицы[ОчередьДляОчистки];
		Узел = ПланыОбмена.ОбновлениеИнформационнойБазы.УзелПоОчереди(ОчередьДляОчистки);
		Для Каждого ПолноеИмяТаблицы Из ВсеЧитаемыеИИзменяемыеТаблицы Цикл
			Если ОбъектыОчереди.Найти(ПолноеИмяТаблицы) <> Неопределено Тогда
				// Не очищаем таблицы, для которых есть обработчики.
				Продолжить;
			КонецЕсли;
			
			Объект = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяТаблицы);
			Если Объект = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			СоставПланаОбмена = Метаданные.ПланыОбмена.ОбновлениеИнформационнойБазы.Состав;
			Если Не СоставПланаОбмена.Содержит(Объект) Тогда
				Продолжить;
			КонецЕсли;
			
			ПланыОбмена.УдалитьРегистрациюИзменений(Узел, Объект);
		КонецЦикла;
		
		Обработанные.Добавить(ОчередьДляОчистки);
	КонецЦикла;
	
КонецПроцедуры

Функция ЧистаяДлительностьВыполненияОбработчика(ОбработчикОбновления, СтатистикаВыполнения)
	
	ЧистаяДлительность = 0;
	Если ОбработчикОбновления.Многопоточный
		И СтатистикаВыполнения <> Неопределено
		И СтатистикаВыполнения["НачалоПроцедурыОбработчика"] <> Неопределено
		И ТипЗнч(СтатистикаВыполнения["НачалоПроцедурыОбработчика"]) = Тип("Массив")
		И СтатистикаВыполнения["ЗавершениеПроцедурыОбработчика"] <> Неопределено
		И ТипЗнч(СтатистикаВыполнения["ЗавершениеПроцедурыОбработчика"]) = Тип("Массив")
		И СтатистикаВыполнения["НачалоПроцедурыОбработчика"].Количество() = СтатистикаВыполнения["ЗавершениеПроцедурыОбработчика"].Количество() Тогда
		ЧислоЗапусков = СтатистикаВыполнения["НачалоПроцедурыОбработчика"].Количество();
		ТаблицаДлительностей = Новый ТаблицаЗначений;
		ТаблицаДлительностей.Колонки.Добавить("Начало");
		ТаблицаДлительностей.Колонки.Добавить("Конец");
		
		Для Итератор = 1 По ЧислоЗапусков Цикл
			ТаблицаДлительностей.Добавить();
		КонецЦикла;
		
		ТаблицаДлительностей.ЗагрузитьКолонку(СтатистикаВыполнения["НачалоПроцедурыОбработчика"], "Начало");
		ТаблицаДлительностей.ЗагрузитьКолонку(СтатистикаВыполнения["ЗавершениеПроцедурыОбработчика"], "Конец");
		ТаблицаДлительностей.Сортировать("Начало Возр");
		
		НачалоТекущегоИнтервала = Неопределено;
		КонецТекущегоИнтервала  = Неопределено;
		Для Каждого Строка Из ТаблицаДлительностей Цикл
			Если Не ЗначениеЗаполнено(Строка.Начало)
				Или Не ЗначениеЗаполнено(Строка.Конец) Тогда
				Продолжить;
			КонецЕсли;
			Если НачалоТекущегоИнтервала = Неопределено Тогда
				НачалоТекущегоИнтервала = Строка.Начало;
				КонецТекущегоИнтервала = Строка.Конец;
				Продолжить;
			КонецЕсли;
			
			Если Строка.Конец < КонецТекущегоИнтервала Тогда
				Продолжить;
			КонецЕсли;
			
			Если Строка.Начало < КонецТекущегоИнтервала Тогда
				Если Строка.Конец > КонецТекущегоИнтервала Тогда
					КонецТекущегоИнтервала = Строка.Конец;
				КонецЕсли;
				Продолжить;
			КонецЕсли;
			
			Если Строка.Начало > КонецТекущегоИнтервала Тогда
				ЧистаяДлительность = ЧистаяДлительность + (КонецТекущегоИнтервала - НачалоТекущегоИнтервала);
				НачалоТекущегоИнтервала = Строка.Начало;
				КонецТекущегоИнтервала = Строка.Конец;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат ЧистаяДлительность * 1000;
	
КонецФункции

Процедура ЗаписатьПрогрессВыполненияОбработчика(ПараметрыОбработчика) Экспорт
	
	Если ПараметрыОбработчика = Неопределено
		Или ПараметрыОбработчика.ОбработаноОбъектов = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ТекущаяДата = ТекущаяДатаСеанса();
	ИнтервалЧас = НачалоЧаса(ТекущаяДата);
	
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.ПрогрессОбновления");
	ЭлементБлокировкиДанных.УстановитьЗначение("ИмяОбработчика", ПараметрыОбработчика.ИмяОбработчика);
	ЭлементБлокировкиДанных.УстановитьЗначение("КлючЗаписи", ПараметрыОбработчика.КлючЗаписиПрогрессаОбновления);
	ЭлементБлокировкиДанных.УстановитьЗначение("ИнтервалЧас", ИнтервалЧас);
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать();
		
		НаборЗаписей = РегистрыСведений.ПрогрессОбновления.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.ИмяОбработчика.Установить(ПараметрыОбработчика.ИмяОбработчика);
		НаборЗаписей.Отбор.КлючЗаписи.Установить(ПараметрыОбработчика.КлючЗаписиПрогрессаОбновления);
		НаборЗаписей.Отбор.ИнтервалЧас.Установить(ИнтервалЧас);
		НаборЗаписей.Прочитать();
		Если НаборЗаписей.Количество() = 0 Тогда
			Запись = НаборЗаписей.Добавить();
			Запись.ИмяОбработчика = ПараметрыОбработчика.ИмяОбработчика;
			Запись.КлючЗаписи = ПараметрыОбработчика.КлючЗаписиПрогрессаОбновления;
			Запись.ИнтервалЧас = ИнтервалЧас;
			Запись.ОбъектовОбработано = ПараметрыОбработчика.ОбработаноОбъектов;
		Иначе
			Запись = НаборЗаписей[0];
			Запись.ОбъектовОбработано = Запись.ОбъектовОбработано + ПараметрыОбработчика.ОбработаноОбъектов;
		КонецЕсли;
		НаборЗаписей.ОбменДанными.Загрузка = Истина;
		НаборЗаписей.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
		НаборЗаписей.Записать();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

#КонецОбласти